xref: /openbmc/linux/drivers/bus/vexpress-config.c (revision eb3fcf00)
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 
21 struct vexpress_config_bridge {
22 	struct vexpress_config_bridge_ops *ops;
23 	void *context;
24 };
25 
26 
27 static DEFINE_MUTEX(vexpress_config_mutex);
28 static struct class *vexpress_config_class;
29 static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER;
30 
31 
32 void vexpress_config_set_master(u32 site)
33 {
34 	vexpress_config_site_master = site;
35 }
36 
37 u32 vexpress_config_get_master(void)
38 {
39 	return vexpress_config_site_master;
40 }
41 
42 void vexpress_config_lock(void *arg)
43 {
44 	mutex_lock(&vexpress_config_mutex);
45 }
46 
47 void vexpress_config_unlock(void *arg)
48 {
49 	mutex_unlock(&vexpress_config_mutex);
50 }
51 
52 
53 static 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 
69 int 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 
84 static 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 
92 struct 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 }
121 EXPORT_SYMBOL_GPL(devm_regmap_init_vexpress_config);
122 
123 struct 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 
160 static 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 
170 static 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 	if (WARN_ON(!parent))
182 		return -ENODEV;
183 
184 	return of_platform_populate(node, NULL, NULL, parent);
185 }
186 
187 static int __init vexpress_config_init(void)
188 {
189 	int err = 0;
190 	struct device_node *node;
191 
192 	/* Need the config devices early, before the "normal" devices... */
193 	for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") {
194 		err = vexpress_config_populate(node);
195 		if (err)
196 			break;
197 	}
198 
199 	return err;
200 }
201 postcore_initcall(vexpress_config_init);
202 
203