linux/drivers/bus/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) 2014 ARM Limited
  12 */
  13
  14#include <linux/err.h>
  15#include <linux/init.h>
  16#include <linux/of.h>
  17#include <linux/of_device.h>
  18#include <linux/vexpress.h>
  19
  20
  21struct vexpress_config_bridge {
  22        struct vexpress_config_bridge_ops *ops;
  23        void *context;
  24};
  25
  26
  27static DEFINE_MUTEX(vexpress_config_mutex);
  28static struct class *vexpress_config_class;
  29static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER;
  30
  31
  32void vexpress_config_set_master(u32 site)
  33{
  34        vexpress_config_site_master = site;
  35}
  36
  37u32 vexpress_config_get_master(void)
  38{
  39        return vexpress_config_site_master;
  40}
  41
  42void vexpress_config_lock(void *arg)
  43{
  44        mutex_lock(&vexpress_config_mutex);
  45}
  46
  47void vexpress_config_unlock(void *arg)
  48{
  49        mutex_unlock(&vexpress_config_mutex);
  50}
  51
  52
  53static void vexpress_config_find_prop(struct device_node *node,
  54                const char *name, u32 *val)
  55{
  56        /* Default value */
  57        *val = 0;
  58
  59        of_node_get(node);
  60        while (node) {
  61                if (of_property_read_u32(node, name, val) == 0) {
  62                        of_node_put(node);
  63                        return;
  64                }
  65                node = of_get_next_parent(node);
  66        }
  67}
  68
  69int vexpress_config_get_topo(struct device_node *node, u32 *site,
  70                u32 *position, u32 *dcc)
  71{
  72        vexpress_config_find_prop(node, "arm,vexpress,site", site);
  73        if (*site == VEXPRESS_SITE_MASTER)
  74                *site = vexpress_config_site_master;
  75        if (WARN_ON(vexpress_config_site_master == VEXPRESS_SITE_MASTER))
  76                return -EINVAL;
  77        vexpress_config_find_prop(node, "arm,vexpress,position", position);
  78        vexpress_config_find_prop(node, "arm,vexpress,dcc", dcc);
  79
  80        return 0;
  81}
  82
  83
  84static void vexpress_config_devres_release(struct device *dev, void *res)
  85{
  86        struct vexpress_config_bridge *bridge = dev_get_drvdata(dev->parent);
  87        struct regmap *regmap = res;
  88
  89        bridge->ops->regmap_exit(regmap, bridge->context);
  90}
  91
  92struct regmap *devm_regmap_init_vexpress_config(struct device *dev)
  93{
  94        struct vexpress_config_bridge *bridge;
  95        struct regmap *regmap;
  96        struct regmap **res;
  97
  98        if (WARN_ON(dev->parent->class != vexpress_config_class))
  99                return ERR_PTR(-ENODEV);
 100
 101        bridge = dev_get_drvdata(dev->parent);
 102        if (WARN_ON(!bridge))
 103                return ERR_PTR(-EINVAL);
 104
 105        res = devres_alloc(vexpress_config_devres_release, sizeof(*res),
 106                        GFP_KERNEL);
 107        if (!res)
 108                return ERR_PTR(-ENOMEM);
 109
 110        regmap = (bridge->ops->regmap_init)(dev, bridge->context);
 111        if (IS_ERR(regmap)) {
 112                devres_free(res);
 113                return regmap;
 114        }
 115
 116        *res = regmap;
 117        devres_add(dev, res);
 118
 119        return regmap;
 120}
 121EXPORT_SYMBOL_GPL(devm_regmap_init_vexpress_config);
 122
 123struct device *vexpress_config_bridge_register(struct device *parent,
 124                struct vexpress_config_bridge_ops *ops, void *context)
 125{
 126        struct device *dev;
 127        struct vexpress_config_bridge *bridge;
 128
 129        if (!vexpress_config_class) {
 130                vexpress_config_class = class_create(THIS_MODULE,
 131                                "vexpress-config");
 132                if (IS_ERR(vexpress_config_class))
 133                        return (void *)vexpress_config_class;
 134        }
 135
 136        dev = device_create(vexpress_config_class, parent, 0,
 137                        NULL, "%s.bridge", dev_name(parent));
 138
 139        if (IS_ERR(dev))
 140                return dev;
 141
 142        bridge = devm_kmalloc(dev, sizeof(*bridge), GFP_KERNEL);
 143        if (!bridge) {
 144                put_device(dev);
 145                device_unregister(dev);
 146                return ERR_PTR(-ENOMEM);
 147        }
 148        bridge->ops = ops;
 149        bridge->context = context;
 150
 151        dev_set_drvdata(dev, bridge);
 152
 153        dev_dbg(parent, "Registered bridge '%s', parent node %p\n",
 154                        dev_name(dev), parent->of_node);
 155
 156        return dev;
 157}
 158
 159
 160static int vexpress_config_node_match(struct device *dev, const void *data)
 161{
 162        const struct device_node *node = data;
 163
 164        dev_dbg(dev, "Parent node %p, looking for %p\n",
 165                        dev->parent->of_node, node);
 166
 167        return dev->parent->of_node == node;
 168}
 169
 170static int vexpress_config_populate(struct device_node *node)
 171{
 172        struct device_node *bridge;
 173        struct device *parent;
 174
 175        bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0);
 176        if (!bridge)
 177                return -EINVAL;
 178
 179        parent = class_find_device(vexpress_config_class, NULL, bridge,
 180                        vexpress_config_node_match);
 181        of_node_put(bridge);
 182        if (WARN_ON(!parent))
 183                return -ENODEV;
 184
 185        return of_platform_populate(node, NULL, NULL, parent);
 186}
 187
 188static int __init vexpress_config_init(void)
 189{
 190        int err = 0;
 191        struct device_node *node;
 192
 193        /* Need the config devices early, before the "normal" devices... */
 194        for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") {
 195                err = vexpress_config_populate(node);
 196                if (err) {
 197                        of_node_put(node);
 198                        break;
 199                }
 200        }
 201
 202        return err;
 203}
 204postcore_initcall(vexpress_config_init);
 205
 206