blob: ff70575b2db68316ed19d8dbaa322a82108856c0 [file] [log] [blame]
Thomas Gleixner1802d0b2019-05-27 08:55:21 +02001// SPDX-License-Identifier: GPL-2.0-only
Pawel Moll3b9334a2014-04-30 16:46:29 +01002/*
Pawel Moll3b9334a2014-04-30 16:46:29 +01003 *
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
14struct vexpress_config_bridge {
15 struct vexpress_config_bridge_ops *ops;
16 void *context;
17};
18
19
20static DEFINE_MUTEX(vexpress_config_mutex);
21static struct class *vexpress_config_class;
22static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER;
23
24
25void vexpress_config_set_master(u32 site)
26{
27 vexpress_config_site_master = site;
28}
29
30u32 vexpress_config_get_master(void)
31{
32 return vexpress_config_site_master;
33}
34
35void vexpress_config_lock(void *arg)
36{
37 mutex_lock(&vexpress_config_mutex);
38}
39
40void vexpress_config_unlock(void *arg)
41{
42 mutex_unlock(&vexpress_config_mutex);
43}
44
45
46static 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
62int 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
77static 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
85struct 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
Nicolas Boichatbbb4d8722015-07-08 14:30:16 +0800103 regmap = (bridge->ops->regmap_init)(dev, bridge->context);
Pawel Moll3b9334a2014-04-30 16:46:29 +0100104 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}
Arnd Bergmannb33cdd22014-05-26 17:25:22 +0200114EXPORT_SYMBOL_GPL(devm_regmap_init_vexpress_config);
Pawel Moll3b9334a2014-04-30 16:46:29 +0100115
116struct 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
153static 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
163static int vexpress_config_populate(struct device_node *node)
164{
165 struct device_node *bridge;
166 struct device *parent;
Johan Hovoldc0909592016-11-16 17:31:30 +0000167 int ret;
Pawel Moll3b9334a2014-04-30 16:46:29 +0100168
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);
Peter Chen557e37c2016-07-01 17:41:59 +0800175 of_node_put(bridge);
Pawel Moll3b9334a2014-04-30 16:46:29 +0100176 if (WARN_ON(!parent))
177 return -ENODEV;
178
Johan Hovoldc0909592016-11-16 17:31:30 +0000179 ret = of_platform_populate(node, NULL, NULL, parent);
180
181 put_device(parent);
182
183 return ret;
Pawel Moll3b9334a2014-04-30 16:46:29 +0100184}
185
186static 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);
Amitoj Kaur Chawlad99875e2016-01-22 23:38:38 +0530194 if (err) {
195 of_node_put(node);
Pawel Moll3b9334a2014-04-30 16:46:29 +0100196 break;
Amitoj Kaur Chawlad99875e2016-01-22 23:38:38 +0530197 }
Pawel Moll3b9334a2014-04-30 16:46:29 +0100198 }
199
200 return err;
201}
202postcore_initcall(vexpress_config_init);
203