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        vexpress_config_dump_trans("Executing", trans);
 188
 189        if (list_empty(&bridge->transactions))
 190                status = bridge->info->func_exec(trans->func->func,
 191                                trans->offset, trans->write, trans->data);
 192        else
 193                status = VEXPRESS_CONFIG_STATUS_WAIT;
 194
 195        switch (status) {
 196        case VEXPRESS_CONFIG_STATUS_DONE:
 197                vexpress_config_dump_trans("Finished", trans);
 198                trans->status = status;
 199                break;
 200        case VEXPRESS_CONFIG_STATUS_WAIT:
 201                list_add_tail(&trans->list, &bridge->transactions);
 202                break;
 203        }
 204
 205        spin_unlock_irqrestore(&bridge->transactions_lock, flags);
 206
 207        return status;
 208}
 209
 210void vexpress_config_complete(struct vexpress_config_bridge *bridge,
 211                int status)
 212{
 213        struct vexpress_config_trans *trans;
 214        unsigned long flags;
 215
 216        spin_lock_irqsave(&bridge->transactions_lock, flags);
 217
 218        trans = list_first_entry(&bridge->transactions,
 219                        struct vexpress_config_trans, list);
 220        vexpress_config_dump_trans("Completed", trans);
 221
 222        trans->status = status;
 223        list_del(&trans->list);
 224
 225        if (!list_empty(&bridge->transactions)) {
 226                vexpress_config_dump_trans("Pending", trans);
 227
 228                bridge->info->func_exec(trans->func->func, trans->offset,
 229                                trans->write, trans->data);
 230        }
 231        spin_unlock_irqrestore(&bridge->transactions_lock, flags);
 232
 233        complete(&trans->completion);
 234}
 235EXPORT_SYMBOL(vexpress_config_complete);
 236
 237int vexpress_config_wait(struct vexpress_config_trans *trans)
 238{
 239        wait_for_completion(&trans->completion);
 240
 241        return trans->status;
 242}
 243EXPORT_SYMBOL(vexpress_config_wait);
 244
 245int vexpress_config_read(struct vexpress_config_func *func, int offset,
 246                u32 *data)
 247{
 248        struct vexpress_config_trans trans = {
 249                .func = func,
 250                .offset = offset,
 251                .write = false,
 252                .data = data,
 253                .status = 0,
 254        };
 255        int status = vexpress_config_schedule(&trans);
 256
 257        if (status == VEXPRESS_CONFIG_STATUS_WAIT)
 258                status = vexpress_config_wait(&trans);
 259
 260        return status;
 261}
 262EXPORT_SYMBOL(vexpress_config_read);
 263
 264int vexpress_config_write(struct vexpress_config_func *func, int offset,
 265                u32 data)
 266{
 267        struct vexpress_config_trans trans = {
 268                .func = func,
 269                .offset = offset,
 270                .write = true,
 271                .data = &data,
 272                .status = 0,
 273        };
 274        int status = vexpress_config_schedule(&trans);
 275
 276        if (status == VEXPRESS_CONFIG_STATUS_WAIT)
 277                status = vexpress_config_wait(&trans);
 278
 279        return status;
 280}
 281EXPORT_SYMBOL(vexpress_config_write);
 282
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.