aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig9
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/vexpress-syscfg.c324
3 files changed, 334 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 8baff0effc7d..d9663ef90ce8 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -515,6 +515,15 @@ config SRAM
515 the genalloc API. It is supposed to be used for small on-chip SRAM 515 the genalloc API. It is supposed to be used for small on-chip SRAM
516 areas found on many SoCs. 516 areas found on many SoCs.
517 517
518config VEXPRESS_SYSCFG
519 bool "Versatile Express System Configuration driver"
520 depends on VEXPRESS_CONFIG
521 default y
522 help
523 ARM Ltd. Versatile Express uses specialised platform configuration
524 bus. System Configuration interface is one of the possible means
525 of generating transactions on this bus.
526
518source "drivers/misc/c2port/Kconfig" 527source "drivers/misc/c2port/Kconfig"
519source "drivers/misc/eeprom/Kconfig" 528source "drivers/misc/eeprom/Kconfig"
520source "drivers/misc/cb710/Kconfig" 529source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7eb4b69580c0..d59ce1261b38 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_SRAM) += sram.o
55obj-y += mic/ 55obj-y += mic/
56obj-$(CONFIG_GENWQE) += genwqe/ 56obj-$(CONFIG_GENWQE) += genwqe/
57obj-$(CONFIG_ECHO) += echo/ 57obj-$(CONFIG_ECHO) += echo/
58obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
diff --git a/drivers/misc/vexpress-syscfg.c b/drivers/misc/vexpress-syscfg.c
new file mode 100644
index 000000000000..73068e50e56d
--- /dev/null
+++ b/drivers/misc/vexpress-syscfg.c
@@ -0,0 +1,324 @@
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/delay.h>
15#include <linux/err.h>
16#include <linux/io.h>
17#include <linux/of.h>
18#include <linux/platform_device.h>
19#include <linux/sched.h>
20#include <linux/slab.h>
21#include <linux/syscore_ops.h>
22#include <linux/vexpress.h>
23
24
25#define SYS_CFGDATA 0x0
26
27#define SYS_CFGCTRL 0x4
28#define SYS_CFGCTRL_START (1 << 31)
29#define SYS_CFGCTRL_WRITE (1 << 30)
30#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26)
31#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20)
32#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16)
33#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12)
34#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0)
35
36#define SYS_CFGSTAT 0x8
37#define SYS_CFGSTAT_ERR (1 << 1)
38#define SYS_CFGSTAT_COMPLETE (1 << 0)
39
40
41struct vexpress_syscfg {
42 struct device *dev;
43 void __iomem *base;
44 struct list_head funcs;
45};
46
47struct vexpress_syscfg_func {
48 struct list_head list;
49 struct vexpress_syscfg *syscfg;
50 struct regmap *regmap;
51 int num_templates;
52 u32 template[0]; /* Keep it last! */
53};
54
55
56static int vexpress_syscfg_exec(struct vexpress_syscfg_func *func,
57 int index, bool write, u32 *data)
58{
59 struct vexpress_syscfg *syscfg = func->syscfg;
60 u32 command, status;
61 int tries;
62 long timeout;
63
64 if (WARN_ON(index > func->num_templates))
65 return -EINVAL;
66
67 command = readl(syscfg->base + SYS_CFGCTRL);
68 if (WARN_ON(command & SYS_CFGCTRL_START))
69 return -EBUSY;
70
71 command = func->template[index];
72 command |= SYS_CFGCTRL_START;
73 command |= write ? SYS_CFGCTRL_WRITE : 0;
74
75 /* Use a canary for reads */
76 if (!write)
77 *data = 0xdeadbeef;
78
79 dev_dbg(syscfg->dev, "func %p, command %x, data %x\n",
80 func, command, *data);
81 writel(*data, syscfg->base + SYS_CFGDATA);
82 writel(0, syscfg->base + SYS_CFGSTAT);
83 writel(command, syscfg->base + SYS_CFGCTRL);
84 mb();
85
86 /* The operation can take ages... Go to sleep, 100us initially */
87 tries = 100;
88 timeout = 100;
89 do {
90 if (!irqs_disabled()) {
91 set_current_state(TASK_INTERRUPTIBLE);
92 schedule_timeout(usecs_to_jiffies(timeout));
93 if (signal_pending(current))
94 return -EINTR;
95 } else {
96 udelay(timeout);
97 }
98
99 status = readl(syscfg->base + SYS_CFGSTAT);
100 if (status & SYS_CFGSTAT_ERR)
101 return -EFAULT;
102
103 if (timeout > 20)
104 timeout -= 20;
105 } while (--tries && !(status & SYS_CFGSTAT_COMPLETE));
106 if (WARN_ON_ONCE(!tries))
107 return -ETIMEDOUT;
108
109 if (!write) {
110 *data = readl(syscfg->base + SYS_CFGDATA);
111 dev_dbg(syscfg->dev, "func %p, read data %x\n", func, *data);
112 }
113
114 return 0;
115}
116
117static int vexpress_syscfg_read(void *context, unsigned int index,
118 unsigned int *val)
119{
120 struct vexpress_syscfg_func *func = context;
121
122 return vexpress_syscfg_exec(func, index, false, val);
123}
124
125static int vexpress_syscfg_write(void *context, unsigned int index,
126 unsigned int val)
127{
128 struct vexpress_syscfg_func *func = context;
129
130 return vexpress_syscfg_exec(func, index, true, &val);
131}
132
133struct regmap_config vexpress_syscfg_regmap_config = {
134 .lock = vexpress_config_lock,
135 .unlock = vexpress_config_unlock,
136 .reg_bits = 32,
137 .val_bits = 32,
138 .reg_read = vexpress_syscfg_read,
139 .reg_write = vexpress_syscfg_write,
140 .reg_format_endian = REGMAP_ENDIAN_LITTLE,
141 .val_format_endian = REGMAP_ENDIAN_LITTLE,
142};
143
144
145static struct regmap *vexpress_syscfg_regmap_init(struct device *dev,
146 void *context)
147{
148 struct platform_device *pdev = to_platform_device(dev);
149 struct vexpress_syscfg *syscfg = context;
150 struct vexpress_syscfg_func *func;
151 struct property *prop;
152 const __be32 *val = NULL;
153 __be32 energy_quirk[4];
154 int num;
155 u32 site, position, dcc;
156 int i;
157
158 if (dev->of_node) {
159 int err = vexpress_config_get_topo(dev->of_node, &site,
160 &position, &dcc);
161
162 if (err)
163 return ERR_PTR(err);
164
165 prop = of_find_property(dev->of_node,
166 "arm,vexpress-sysreg,func", NULL);
167 if (!prop)
168 return ERR_PTR(-EINVAL);
169
170 num = prop->length / sizeof(u32) / 2;
171 val = prop->value;
172 } else {
173 if (pdev->num_resources != 1 ||
174 pdev->resource[0].flags != IORESOURCE_BUS)
175 return ERR_PTR(-EFAULT);
176
177 site = pdev->resource[0].start;
178 if (site == VEXPRESS_SITE_MASTER)
179 site = vexpress_config_get_master();
180 position = 0;
181 dcc = 0;
182 num = 1;
183 }
184
185 /*
186 * "arm,vexpress-energy" function used to be described
187 * by its first device only, now it requires both
188 */
189 if (num == 1 && of_device_is_compatible(dev->of_node,
190 "arm,vexpress-energy")) {
191 num = 2;
192 energy_quirk[0] = *val;
193 energy_quirk[2] = *val++;
194 energy_quirk[1] = *val;
195 energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1);
196 val = energy_quirk;
197 }
198
199 func = kzalloc(sizeof(*func) + sizeof(*func->template) * num,
200 GFP_KERNEL);
201 if (!func)
202 return NULL;
203
204 func->syscfg = syscfg;
205 func->num_templates = num;
206
207 for (i = 0; i < num; i++) {
208 u32 function, device;
209
210 if (dev->of_node) {
211 function = be32_to_cpup(val++);
212 device = be32_to_cpup(val++);
213 } else {
214 function = pdev->resource[0].end;
215 device = pdev->id;
216 }
217
218 dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n",
219 func, site, position, dcc,
220 function, device);
221
222 func->template[i] = SYS_CFGCTRL_DCC(dcc);
223 func->template[i] |= SYS_CFGCTRL_SITE(site);
224 func->template[i] |= SYS_CFGCTRL_POSITION(position);
225 func->template[i] |= SYS_CFGCTRL_FUNC(function);
226 func->template[i] |= SYS_CFGCTRL_DEVICE(device);
227 }
228
229 vexpress_syscfg_regmap_config.max_register = num - 1;
230
231 func->regmap = regmap_init(dev, NULL, func,
232 &vexpress_syscfg_regmap_config);
233
234 if (IS_ERR(func->regmap))
235 kfree(func);
236 else
237 list_add(&func->list, &syscfg->funcs);
238
239 return func->regmap;
240}
241
242static void vexpress_syscfg_regmap_exit(struct regmap *regmap, void *context)
243{
244 struct vexpress_syscfg *syscfg = context;
245 struct vexpress_syscfg_func *func, *tmp;
246
247 regmap_exit(regmap);
248
249 list_for_each_entry_safe(func, tmp, &syscfg->funcs, list) {
250 if (func->regmap == regmap) {
251 list_del(&syscfg->funcs);
252 kfree(func);
253 break;
254 }
255 }
256}
257
258static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = {
259 .regmap_init = vexpress_syscfg_regmap_init,
260 .regmap_exit = vexpress_syscfg_regmap_exit,
261};
262
263
264/* Non-DT hack, to be gone... */
265static struct device *vexpress_syscfg_bridge;
266
267int vexpress_syscfg_device_register(struct platform_device *pdev)
268{
269 pdev->dev.parent = vexpress_syscfg_bridge;
270
271 return platform_device_register(pdev);
272}
273
274
275int vexpress_syscfg_probe(struct platform_device *pdev)
276{
277 struct vexpress_syscfg *syscfg;
278 struct resource *res;
279 struct device *bridge;
280
281 syscfg = devm_kzalloc(&pdev->dev, sizeof(*syscfg), GFP_KERNEL);
282 if (!syscfg)
283 return -ENOMEM;
284 syscfg->dev = &pdev->dev;
285 INIT_LIST_HEAD(&syscfg->funcs);
286
287 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
288 if (!devm_request_mem_region(&pdev->dev, res->start,
289 resource_size(res), pdev->name))
290 return -EBUSY;
291
292 syscfg->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
293 if (!syscfg->base)
294 return -EFAULT;
295
296 /* Must use dev.parent (MFD), as that's where DT phandle points at... */
297 bridge = vexpress_config_bridge_register(pdev->dev.parent,
298 &vexpress_syscfg_bridge_ops, syscfg);
299 if (IS_ERR(bridge))
300 return PTR_ERR(bridge);
301
302 /* Non-DT case */
303 if (!pdev->dev.of_node)
304 vexpress_syscfg_bridge = bridge;
305
306 return 0;
307}
308
309static const struct platform_device_id vexpress_syscfg_id_table[] = {
310 { "vexpress-syscfg", },
311 {},
312};
313
314static struct platform_driver vexpress_syscfg_driver = {
315 .driver.name = "vexpress-syscfg",
316 .id_table = vexpress_syscfg_id_table,
317 .probe = vexpress_syscfg_probe,
318};
319
320static int __init vexpress_syscfg_init(void)
321{
322 return platform_driver_register(&vexpress_syscfg_driver);
323}
324core_initcall(vexpress_syscfg_init);