diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-02 19:14:07 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-02 19:14:07 -0400 |
commit | 0a58471541cc823ef8056d23945c39fec154481c (patch) | |
tree | 04a8499be0659ac16f82f3b0d0d8d2c2ccafe4dd /drivers/misc | |
parent | ff933a0817f95efbeb97bec5ca609a13f8aed686 (diff) | |
parent | 08d38bebb4dcd6414944f8277ea5ea30010664fe (diff) |
Merge tag 'cleanup-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc into next
Pull ARM SoC cleanups from Olof Johansson:
"Cleanups for 3.16. Among these are:
- a bunch of misc cleanups for Broadcom platforms, mostly
housekeeping
- enabling Common Clock Framework on the older s3c24xx Samsung
chipsets
- cleanup of the Versatile Express system controller code, moving it
to syscon
- power management cleanups for OMAP platforms
plus a handful of other cleanups across the place"
* tag 'cleanup-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (87 commits)
ARM: kconfig: allow PCI support to be selected with ARCH_MULTIPLATFORM
clk: samsung: fix build error
ARM: vexpress: refine dependencies for new code
clk: samsung: clk-s3c2410-dlck: do not use PNAME macro as it declares __initdata
cpufreq: exynos: Fix the compile error
ARM: S3C24XX: move debug-macro.S into the common space
ARM: S3C24XX: use generic DEBUG_UART_PHY/_VIRT in debug macro
ARM: S3C24XX: trim down debug uart handling
ARM: compressed/head.S: remove s3c24xx special case
ARM: EXYNOS: Remove unnecessary inclusion of cpu.h
ARM: EXYNOS: Migrate Exynos specific macros from plat to mach
ARM: EXYNOS: Remove exynos_subsys registration
ARM: EXYNOS: Remove duplicate lines in Makefile
ARM: EXYNOS: use v7_exit_coherency_flush macro for cache disabling
ARM: OMAP4: PRCM: remove references to cm-regbits-44xx.h from PRCM core files
ARM: OMAP3/4: PRM: add support of late_init call to prm_ll_ops
ARM: OMAP3/OMAP4: PRM: add prm_features flags and add IO wakeup under it
ARM: OMAP3/4: PRM: provide io chain reconfig function through irq setup
ARM: OMAP2+: PRM: remove unnecessary cpu_is_XXX calls from prm_init / exit
ARM: OMAP2+: PRCM: cleanup some header includes
...
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/vexpress-syscfg.c | 324 |
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 | ||
518 | config 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 | |||
518 | source "drivers/misc/c2port/Kconfig" | 527 | source "drivers/misc/c2port/Kconfig" |
519 | source "drivers/misc/eeprom/Kconfig" | 528 | source "drivers/misc/eeprom/Kconfig" |
520 | source "drivers/misc/cb710/Kconfig" | 529 | source "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 | |||
55 | obj-y += mic/ | 55 | obj-y += mic/ |
56 | obj-$(CONFIG_GENWQE) += genwqe/ | 56 | obj-$(CONFIG_GENWQE) += genwqe/ |
57 | obj-$(CONFIG_ECHO) += echo/ | 57 | obj-$(CONFIG_ECHO) += echo/ |
58 | obj-$(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 | |||
41 | struct vexpress_syscfg { | ||
42 | struct device *dev; | ||
43 | void __iomem *base; | ||
44 | struct list_head funcs; | ||
45 | }; | ||
46 | |||
47 | struct 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 | |||
56 | static 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 | |||
117 | static 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 | |||
125 | static 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 | |||
133 | struct 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 | |||
145 | static 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 | |||
242 | static 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 | |||
258 | static 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... */ | ||
265 | static struct device *vexpress_syscfg_bridge; | ||
266 | |||
267 | int 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 | |||
275 | int 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 | |||
309 | static const struct platform_device_id vexpress_syscfg_id_table[] = { | ||
310 | { "vexpress-syscfg", }, | ||
311 | {}, | ||
312 | }; | ||
313 | |||
314 | static 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 | |||
320 | static int __init vexpress_syscfg_init(void) | ||
321 | { | ||
322 | return platform_driver_register(&vexpress_syscfg_driver); | ||
323 | } | ||
324 | core_initcall(vexpress_syscfg_init); | ||