xref: /openbmc/linux/drivers/bus/vexpress-config.c (revision bbb4d872)
13b9334acSPawel Moll /*
23b9334acSPawel Moll  * This program is free software; you can redistribute it and/or modify
33b9334acSPawel Moll  * it under the terms of the GNU General Public License version 2 as
43b9334acSPawel Moll  * published by the Free Software Foundation.
53b9334acSPawel Moll  *
63b9334acSPawel Moll  * This program is distributed in the hope that it will be useful,
73b9334acSPawel Moll  * but WITHOUT ANY WARRANTY; without even the implied warranty of
83b9334acSPawel Moll  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
93b9334acSPawel Moll  * GNU General Public License for more details.
103b9334acSPawel Moll  *
113b9334acSPawel Moll  * Copyright (C) 2014 ARM Limited
123b9334acSPawel Moll  */
133b9334acSPawel Moll 
143b9334acSPawel Moll #include <linux/err.h>
153b9334acSPawel Moll #include <linux/init.h>
163b9334acSPawel Moll #include <linux/of.h>
173b9334acSPawel Moll #include <linux/of_device.h>
183b9334acSPawel Moll #include <linux/vexpress.h>
193b9334acSPawel Moll 
203b9334acSPawel Moll 
213b9334acSPawel Moll struct vexpress_config_bridge {
223b9334acSPawel Moll 	struct vexpress_config_bridge_ops *ops;
233b9334acSPawel Moll 	void *context;
243b9334acSPawel Moll };
253b9334acSPawel Moll 
263b9334acSPawel Moll 
273b9334acSPawel Moll static DEFINE_MUTEX(vexpress_config_mutex);
283b9334acSPawel Moll static struct class *vexpress_config_class;
293b9334acSPawel Moll static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER;
303b9334acSPawel Moll 
313b9334acSPawel Moll 
323b9334acSPawel Moll void vexpress_config_set_master(u32 site)
333b9334acSPawel Moll {
343b9334acSPawel Moll 	vexpress_config_site_master = site;
353b9334acSPawel Moll }
363b9334acSPawel Moll 
373b9334acSPawel Moll u32 vexpress_config_get_master(void)
383b9334acSPawel Moll {
393b9334acSPawel Moll 	return vexpress_config_site_master;
403b9334acSPawel Moll }
413b9334acSPawel Moll 
423b9334acSPawel Moll void vexpress_config_lock(void *arg)
433b9334acSPawel Moll {
443b9334acSPawel Moll 	mutex_lock(&vexpress_config_mutex);
453b9334acSPawel Moll }
463b9334acSPawel Moll 
473b9334acSPawel Moll void vexpress_config_unlock(void *arg)
483b9334acSPawel Moll {
493b9334acSPawel Moll 	mutex_unlock(&vexpress_config_mutex);
503b9334acSPawel Moll }
513b9334acSPawel Moll 
523b9334acSPawel Moll 
533b9334acSPawel Moll static void vexpress_config_find_prop(struct device_node *node,
543b9334acSPawel Moll 		const char *name, u32 *val)
553b9334acSPawel Moll {
563b9334acSPawel Moll 	/* Default value */
573b9334acSPawel Moll 	*val = 0;
583b9334acSPawel Moll 
593b9334acSPawel Moll 	of_node_get(node);
603b9334acSPawel Moll 	while (node) {
613b9334acSPawel Moll 		if (of_property_read_u32(node, name, val) == 0) {
623b9334acSPawel Moll 			of_node_put(node);
633b9334acSPawel Moll 			return;
643b9334acSPawel Moll 		}
653b9334acSPawel Moll 		node = of_get_next_parent(node);
663b9334acSPawel Moll 	}
673b9334acSPawel Moll }
683b9334acSPawel Moll 
693b9334acSPawel Moll int vexpress_config_get_topo(struct device_node *node, u32 *site,
703b9334acSPawel Moll 		u32 *position, u32 *dcc)
713b9334acSPawel Moll {
723b9334acSPawel Moll 	vexpress_config_find_prop(node, "arm,vexpress,site", site);
733b9334acSPawel Moll 	if (*site == VEXPRESS_SITE_MASTER)
743b9334acSPawel Moll 		*site = vexpress_config_site_master;
753b9334acSPawel Moll 	if (WARN_ON(vexpress_config_site_master == VEXPRESS_SITE_MASTER))
763b9334acSPawel Moll 		return -EINVAL;
773b9334acSPawel Moll 	vexpress_config_find_prop(node, "arm,vexpress,position", position);
783b9334acSPawel Moll 	vexpress_config_find_prop(node, "arm,vexpress,dcc", dcc);
793b9334acSPawel Moll 
803b9334acSPawel Moll 	return 0;
813b9334acSPawel Moll }
823b9334acSPawel Moll 
833b9334acSPawel Moll 
843b9334acSPawel Moll static void vexpress_config_devres_release(struct device *dev, void *res)
853b9334acSPawel Moll {
863b9334acSPawel Moll 	struct vexpress_config_bridge *bridge = dev_get_drvdata(dev->parent);
873b9334acSPawel Moll 	struct regmap *regmap = res;
883b9334acSPawel Moll 
893b9334acSPawel Moll 	bridge->ops->regmap_exit(regmap, bridge->context);
903b9334acSPawel Moll }
913b9334acSPawel Moll 
923b9334acSPawel Moll struct regmap *devm_regmap_init_vexpress_config(struct device *dev)
933b9334acSPawel Moll {
943b9334acSPawel Moll 	struct vexpress_config_bridge *bridge;
953b9334acSPawel Moll 	struct regmap *regmap;
963b9334acSPawel Moll 	struct regmap **res;
973b9334acSPawel Moll 
983b9334acSPawel Moll 	if (WARN_ON(dev->parent->class != vexpress_config_class))
993b9334acSPawel Moll 		return ERR_PTR(-ENODEV);
1003b9334acSPawel Moll 
1013b9334acSPawel Moll 	bridge = dev_get_drvdata(dev->parent);
1023b9334acSPawel Moll 	if (WARN_ON(!bridge))
1033b9334acSPawel Moll 		return ERR_PTR(-EINVAL);
1043b9334acSPawel Moll 
1053b9334acSPawel Moll 	res = devres_alloc(vexpress_config_devres_release, sizeof(*res),
1063b9334acSPawel Moll 			GFP_KERNEL);
1073b9334acSPawel Moll 	if (!res)
1083b9334acSPawel Moll 		return ERR_PTR(-ENOMEM);
1093b9334acSPawel Moll 
110bbb4d872SNicolas Boichat 	regmap = (bridge->ops->regmap_init)(dev, bridge->context);
1113b9334acSPawel Moll 	if (IS_ERR(regmap)) {
1123b9334acSPawel Moll 		devres_free(res);
1133b9334acSPawel Moll 		return regmap;
1143b9334acSPawel Moll 	}
1153b9334acSPawel Moll 
1163b9334acSPawel Moll 	*res = regmap;
1173b9334acSPawel Moll 	devres_add(dev, res);
1183b9334acSPawel Moll 
1193b9334acSPawel Moll 	return regmap;
1203b9334acSPawel Moll }
121b33cdd28SArnd Bergmann EXPORT_SYMBOL_GPL(devm_regmap_init_vexpress_config);
1223b9334acSPawel Moll 
1233b9334acSPawel Moll struct device *vexpress_config_bridge_register(struct device *parent,
1243b9334acSPawel Moll 		struct vexpress_config_bridge_ops *ops, void *context)
1253b9334acSPawel Moll {
1263b9334acSPawel Moll 	struct device *dev;
1273b9334acSPawel Moll 	struct vexpress_config_bridge *bridge;
1283b9334acSPawel Moll 
1293b9334acSPawel Moll 	if (!vexpress_config_class) {
1303b9334acSPawel Moll 		vexpress_config_class = class_create(THIS_MODULE,
1313b9334acSPawel Moll 				"vexpress-config");
1323b9334acSPawel Moll 		if (IS_ERR(vexpress_config_class))
1333b9334acSPawel Moll 			return (void *)vexpress_config_class;
1343b9334acSPawel Moll 	}
1353b9334acSPawel Moll 
1363b9334acSPawel Moll 	dev = device_create(vexpress_config_class, parent, 0,
1373b9334acSPawel Moll 			NULL, "%s.bridge", dev_name(parent));
1383b9334acSPawel Moll 
1393b9334acSPawel Moll 	if (IS_ERR(dev))
1403b9334acSPawel Moll 		return dev;
1413b9334acSPawel Moll 
1423b9334acSPawel Moll 	bridge = devm_kmalloc(dev, sizeof(*bridge), GFP_KERNEL);
1433b9334acSPawel Moll 	if (!bridge) {
1443b9334acSPawel Moll 		put_device(dev);
1453b9334acSPawel Moll 		device_unregister(dev);
1463b9334acSPawel Moll 		return ERR_PTR(-ENOMEM);
1473b9334acSPawel Moll 	}
1483b9334acSPawel Moll 	bridge->ops = ops;
1493b9334acSPawel Moll 	bridge->context = context;
1503b9334acSPawel Moll 
1513b9334acSPawel Moll 	dev_set_drvdata(dev, bridge);
1523b9334acSPawel Moll 
1533b9334acSPawel Moll 	dev_dbg(parent, "Registered bridge '%s', parent node %p\n",
1543b9334acSPawel Moll 			dev_name(dev), parent->of_node);
1553b9334acSPawel Moll 
1563b9334acSPawel Moll 	return dev;
1573b9334acSPawel Moll }
1583b9334acSPawel Moll 
1593b9334acSPawel Moll 
1603b9334acSPawel Moll static int vexpress_config_node_match(struct device *dev, const void *data)
1613b9334acSPawel Moll {
1623b9334acSPawel Moll 	const struct device_node *node = data;
1633b9334acSPawel Moll 
1643b9334acSPawel Moll 	dev_dbg(dev, "Parent node %p, looking for %p\n",
1653b9334acSPawel Moll 			dev->parent->of_node, node);
1663b9334acSPawel Moll 
1673b9334acSPawel Moll 	return dev->parent->of_node == node;
1683b9334acSPawel Moll }
1693b9334acSPawel Moll 
1703b9334acSPawel Moll static int vexpress_config_populate(struct device_node *node)
1713b9334acSPawel Moll {
1723b9334acSPawel Moll 	struct device_node *bridge;
1733b9334acSPawel Moll 	struct device *parent;
1743b9334acSPawel Moll 
1753b9334acSPawel Moll 	bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0);
1763b9334acSPawel Moll 	if (!bridge)
1773b9334acSPawel Moll 		return -EINVAL;
1783b9334acSPawel Moll 
1793b9334acSPawel Moll 	parent = class_find_device(vexpress_config_class, NULL, bridge,
1803b9334acSPawel Moll 			vexpress_config_node_match);
1813b9334acSPawel Moll 	if (WARN_ON(!parent))
1823b9334acSPawel Moll 		return -ENODEV;
1833b9334acSPawel Moll 
1843b9334acSPawel Moll 	return of_platform_populate(node, NULL, NULL, parent);
1853b9334acSPawel Moll }
1863b9334acSPawel Moll 
1873b9334acSPawel Moll static int __init vexpress_config_init(void)
1883b9334acSPawel Moll {
1893b9334acSPawel Moll 	int err = 0;
1903b9334acSPawel Moll 	struct device_node *node;
1913b9334acSPawel Moll 
1923b9334acSPawel Moll 	/* Need the config devices early, before the "normal" devices... */
1933b9334acSPawel Moll 	for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") {
1943b9334acSPawel Moll 		err = vexpress_config_populate(node);
1953b9334acSPawel Moll 		if (err)
1963b9334acSPawel Moll 			break;
1973b9334acSPawel Moll 	}
1983b9334acSPawel Moll 
1993b9334acSPawel Moll 	return err;
2003b9334acSPawel Moll }
2013b9334acSPawel Moll postcore_initcall(vexpress_config_init);
2023b9334acSPawel Moll 
203