123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * Copyright (C) 2014 ARM Limited
- */
- #include <linux/err.h>
- #include <linux/init.h>
- #include <linux/of.h>
- #include <linux/of_device.h>
- #include <linux/vexpress.h>
- struct vexpress_config_bridge {
- struct vexpress_config_bridge_ops *ops;
- void *context;
- };
- static DEFINE_MUTEX(vexpress_config_mutex);
- static struct class *vexpress_config_class;
- static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER;
- void vexpress_config_set_master(u32 site)
- {
- vexpress_config_site_master = site;
- }
- u32 vexpress_config_get_master(void)
- {
- return vexpress_config_site_master;
- }
- void vexpress_config_lock(void *arg)
- {
- mutex_lock(&vexpress_config_mutex);
- }
- void vexpress_config_unlock(void *arg)
- {
- mutex_unlock(&vexpress_config_mutex);
- }
- static void vexpress_config_find_prop(struct device_node *node,
- const char *name, u32 *val)
- {
- /* Default value */
- *val = 0;
- of_node_get(node);
- while (node) {
- if (of_property_read_u32(node, name, val) == 0) {
- of_node_put(node);
- return;
- }
- node = of_get_next_parent(node);
- }
- }
- int vexpress_config_get_topo(struct device_node *node, u32 *site,
- u32 *position, u32 *dcc)
- {
- vexpress_config_find_prop(node, "arm,vexpress,site", site);
- if (*site == VEXPRESS_SITE_MASTER)
- *site = vexpress_config_site_master;
- if (WARN_ON(vexpress_config_site_master == VEXPRESS_SITE_MASTER))
- return -EINVAL;
- vexpress_config_find_prop(node, "arm,vexpress,position", position);
- vexpress_config_find_prop(node, "arm,vexpress,dcc", dcc);
- return 0;
- }
- static void vexpress_config_devres_release(struct device *dev, void *res)
- {
- struct vexpress_config_bridge *bridge = dev_get_drvdata(dev->parent);
- struct regmap *regmap = res;
- bridge->ops->regmap_exit(regmap, bridge->context);
- }
- struct regmap *devm_regmap_init_vexpress_config(struct device *dev)
- {
- struct vexpress_config_bridge *bridge;
- struct regmap *regmap;
- struct regmap **res;
- if (WARN_ON(dev->parent->class != vexpress_config_class))
- return ERR_PTR(-ENODEV);
- bridge = dev_get_drvdata(dev->parent);
- if (WARN_ON(!bridge))
- return ERR_PTR(-EINVAL);
- res = devres_alloc(vexpress_config_devres_release, sizeof(*res),
- GFP_KERNEL);
- if (!res)
- return ERR_PTR(-ENOMEM);
- regmap = (bridge->ops->regmap_init)(dev, bridge->context);
- if (IS_ERR(regmap)) {
- devres_free(res);
- return regmap;
- }
- *res = regmap;
- devres_add(dev, res);
- return regmap;
- }
- EXPORT_SYMBOL_GPL(devm_regmap_init_vexpress_config);
- struct device *vexpress_config_bridge_register(struct device *parent,
- struct vexpress_config_bridge_ops *ops, void *context)
- {
- struct device *dev;
- struct vexpress_config_bridge *bridge;
- if (!vexpress_config_class) {
- vexpress_config_class = class_create(THIS_MODULE,
- "vexpress-config");
- if (IS_ERR(vexpress_config_class))
- return (void *)vexpress_config_class;
- }
- dev = device_create(vexpress_config_class, parent, 0,
- NULL, "%s.bridge", dev_name(parent));
- if (IS_ERR(dev))
- return dev;
- bridge = devm_kmalloc(dev, sizeof(*bridge), GFP_KERNEL);
- if (!bridge) {
- put_device(dev);
- device_unregister(dev);
- return ERR_PTR(-ENOMEM);
- }
- bridge->ops = ops;
- bridge->context = context;
- dev_set_drvdata(dev, bridge);
- dev_dbg(parent, "Registered bridge '%s', parent node %p\n",
- dev_name(dev), parent->of_node);
- return dev;
- }
- static int vexpress_config_node_match(struct device *dev, const void *data)
- {
- const struct device_node *node = data;
- dev_dbg(dev, "Parent node %p, looking for %p\n",
- dev->parent->of_node, node);
- return dev->parent->of_node == node;
- }
- static int vexpress_config_populate(struct device_node *node)
- {
- struct device_node *bridge;
- struct device *parent;
- int ret;
- bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0);
- if (!bridge)
- return -EINVAL;
- parent = class_find_device(vexpress_config_class, NULL, bridge,
- vexpress_config_node_match);
- of_node_put(bridge);
- if (WARN_ON(!parent))
- return -ENODEV;
- ret = of_platform_populate(node, NULL, NULL, parent);
- put_device(parent);
- return ret;
- }
- static int __init vexpress_config_init(void)
- {
- int err = 0;
- struct device_node *node;
- /* Need the config devices early, before the "normal" devices... */
- for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") {
- err = vexpress_config_populate(node);
- if (err) {
- of_node_put(node);
- break;
- }
- }
- return err;
- }
- postcore_initcall(vexpress_config_init);
|