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