aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/bus
diff options
context:
space:
mode:
authorLorenzo Pieralisi <lorenzo.pieralisi@arm.com>2012-07-13 10:55:52 -0400
committerNicolas Pitre <nicolas.pitre@linaro.org>2013-05-29 15:50:34 -0400
commited69bdd8fd9b2db68b915ce5f60fc51d4744a9b1 (patch)
tree02dc3a5343f662ce153c66cb0e370a49b35b066f /drivers/bus
parent78ecad0183bd7e49131da2b5aa82bee017db1cf0 (diff)
drivers: bus: add ARM CCI support
On ARM multi-cluster systems coherency between cores running on different clusters is managed by the cache-coherent interconnect (CCI). It allows broadcasting of TLB invalidates and memory barriers and it guarantees cache coherency at system level through snooping of slave interfaces connected to it. This patch enables the basic infrastructure required in Linux to handle and programme the CCI component. Non-local variables used by the CCI management functions called by power down function calls after disabling the cache must be flushed out to main memory in advance, otherwise incoherency of those values may occur if they are sitting in the cache of some other CPU when power down functions execute. Driver code ensures that relevant data structures are flushed from inner and outer caches after the driver probe is completed. CCI slave port resources are linked to set of CPUs through bus masters phandle properties that link the interface resources to masters node in the device tree. Documentation describing the CCI DT bindings is provided with the patch. Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Diffstat (limited to 'drivers/bus')
-rw-r--r--drivers/bus/Kconfig7
-rw-r--r--drivers/bus/Makefile2
-rw-r--r--drivers/bus/arm-cci.c426
3 files changed, 435 insertions, 0 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index b05ecab915c4..5286e2d333b0 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -26,4 +26,11 @@ config OMAP_INTERCONNECT
26 26
27 help 27 help
28 Driver to enable OMAP interconnect error handling driver. 28 Driver to enable OMAP interconnect error handling driver.
29
30config ARM_CCI
31 bool "ARM CCI driver support"
32 depends on ARM
33 help
34 Driver supporting the CCI cache coherent interconnect for ARM
35 platforms.
29endmenu 36endmenu
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 3c7b53c12091..670cea443802 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -7,3 +7,5 @@ obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
7 7
8# Interconnect bus driver for OMAP SoCs. 8# Interconnect bus driver for OMAP SoCs.
9obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o 9obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
10# CCI cache coherent interconnect for ARM platforms
11obj-$(CONFIG_ARM_CCI) += arm-cci.o
diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c
new file mode 100644
index 000000000000..ea81fa4a28db
--- /dev/null
+++ b/drivers/bus/arm-cci.c
@@ -0,0 +1,426 @@
1/*
2 * CCI cache coherent interconnect driver
3 *
4 * Copyright (C) 2013 ARM Ltd.
5 * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
12 * kind, whether express or implied; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <linux/arm-cci.h>
18#include <linux/io.h>
19#include <linux/module.h>
20#include <linux/of_address.h>
21#include <linux/slab.h>
22
23#include <asm/cacheflush.h>
24#include <asm/smp_plat.h>
25
26#define CCI_PORT_CTRL 0x0
27#define CCI_CTRL_STATUS 0xc
28
29#define CCI_ENABLE_SNOOP_REQ 0x1
30#define CCI_ENABLE_DVM_REQ 0x2
31#define CCI_ENABLE_REQ (CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ)
32
33struct cci_nb_ports {
34 unsigned int nb_ace;
35 unsigned int nb_ace_lite;
36};
37
38enum cci_ace_port_type {
39 ACE_INVALID_PORT = 0x0,
40 ACE_PORT,
41 ACE_LITE_PORT,
42};
43
44struct cci_ace_port {
45 void __iomem *base;
46 enum cci_ace_port_type type;
47 struct device_node *dn;
48};
49
50static struct cci_ace_port *ports;
51static unsigned int nb_cci_ports;
52
53static void __iomem *cci_ctrl_base;
54
55struct cpu_port {
56 u64 mpidr;
57 u32 port;
58};
59/*
60 * Use the port MSB as valid flag, shift can be made dynamic
61 * by computing number of bits required for port indexes.
62 * Code disabling CCI cpu ports runs with D-cache invalidated
63 * and SCTLR bit clear so data accesses must be kept to a minimum
64 * to improve performance; for now shift is left static to
65 * avoid one more data access while disabling the CCI port.
66 */
67#define PORT_VALID_SHIFT 31
68#define PORT_VALID (0x1 << PORT_VALID_SHIFT)
69
70static inline void init_cpu_port(struct cpu_port *port, u32 index, u64 mpidr)
71{
72 port->port = PORT_VALID | index;
73 port->mpidr = mpidr;
74}
75
76static inline bool cpu_port_is_valid(struct cpu_port *port)
77{
78 return !!(port->port & PORT_VALID);
79}
80
81static inline bool cpu_port_match(struct cpu_port *port, u64 mpidr)
82{
83 return port->mpidr == (mpidr & MPIDR_HWID_BITMASK);
84}
85
86static struct cpu_port cpu_port[NR_CPUS];
87
88/**
89 * __cci_ace_get_port - Function to retrieve the port index connected to
90 * a cpu or device.
91 *
92 * @dn: device node of the device to look-up
93 * @type: port type
94 *
95 * Return value:
96 * - CCI port index if success
97 * - -ENODEV if failure
98 */
99static int __cci_ace_get_port(struct device_node *dn, int type)
100{
101 int i;
102 bool ace_match;
103 struct device_node *cci_portn;
104
105 cci_portn = of_parse_phandle(dn, "cci-control-port", 0);
106 for (i = 0; i < nb_cci_ports; i++) {
107 ace_match = ports[i].type == type;
108 if (ace_match && cci_portn == ports[i].dn)
109 return i;
110 }
111 return -ENODEV;
112}
113
114int cci_ace_get_port(struct device_node *dn)
115{
116 return __cci_ace_get_port(dn, ACE_LITE_PORT);
117}
118EXPORT_SYMBOL_GPL(cci_ace_get_port);
119
120static void __init cci_ace_init_ports(void)
121{
122 int port, ac, cpu;
123 u64 hwid;
124 const u32 *cell;
125 struct device_node *cpun, *cpus;
126
127 cpus = of_find_node_by_path("/cpus");
128 if (WARN(!cpus, "Missing cpus node, bailing out\n"))
129 return;
130
131 if (WARN_ON(of_property_read_u32(cpus, "#address-cells", &ac)))
132 ac = of_n_addr_cells(cpus);
133
134 /*
135 * Port index look-up speeds up the function disabling ports by CPU,
136 * since the logical to port index mapping is done once and does
137 * not change after system boot.
138 * The stashed index array is initialized for all possible CPUs
139 * at probe time.
140 */
141 for_each_child_of_node(cpus, cpun) {
142 if (of_node_cmp(cpun->type, "cpu"))
143 continue;
144 cell = of_get_property(cpun, "reg", NULL);
145 if (WARN(!cell, "%s: missing reg property\n", cpun->full_name))
146 continue;
147
148 hwid = of_read_number(cell, ac);
149 cpu = get_logical_index(hwid & MPIDR_HWID_BITMASK);
150
151 if (cpu < 0 || !cpu_possible(cpu))
152 continue;
153 port = __cci_ace_get_port(cpun, ACE_PORT);
154 if (port < 0)
155 continue;
156
157 init_cpu_port(&cpu_port[cpu], port, cpu_logical_map(cpu));
158 }
159
160 for_each_possible_cpu(cpu) {
161 WARN(!cpu_port_is_valid(&cpu_port[cpu]),
162 "CPU %u does not have an associated CCI port\n",
163 cpu);
164 }
165}
166/*
167 * Functions to enable/disable a CCI interconnect slave port
168 *
169 * They are called by low-level power management code to disable slave
170 * interfaces snoops and DVM broadcast.
171 * Since they may execute with cache data allocation disabled and
172 * after the caches have been cleaned and invalidated the functions provide
173 * no explicit locking since they may run with D-cache disabled, so normal
174 * cacheable kernel locks based on ldrex/strex may not work.
175 * Locking has to be provided by BSP implementations to ensure proper
176 * operations.
177 */
178
179/**
180 * cci_port_control() - function to control a CCI port
181 *
182 * @port: index of the port to setup
183 * @enable: if true enables the port, if false disables it
184 */
185static void notrace cci_port_control(unsigned int port, bool enable)
186{
187 void __iomem *base = ports[port].base;
188
189 writel_relaxed(enable ? CCI_ENABLE_REQ : 0, base + CCI_PORT_CTRL);
190 /*
191 * This function is called from power down procedures
192 * and must not execute any instruction that might
193 * cause the processor to be put in a quiescent state
194 * (eg wfi). Hence, cpu_relax() can not be added to this
195 * read loop to optimize power, since it might hide possibly
196 * disruptive operations.
197 */
198 while (readl_relaxed(cci_ctrl_base + CCI_CTRL_STATUS) & 0x1)
199 ;
200}
201
202/**
203 * cci_disable_port_by_cpu() - function to disable a CCI port by CPU
204 * reference
205 *
206 * @mpidr: mpidr of the CPU whose CCI port should be disabled
207 *
208 * Disabling a CCI port for a CPU implies disabling the CCI port
209 * controlling that CPU cluster. Code disabling CPU CCI ports
210 * must make sure that the CPU running the code is the last active CPU
211 * in the cluster ie all other CPUs are quiescent in a low power state.
212 *
213 * Return:
214 * 0 on success
215 * -ENODEV on port look-up failure
216 */
217int notrace cci_disable_port_by_cpu(u64 mpidr)
218{
219 int cpu;
220 bool is_valid;
221 for (cpu = 0; cpu < nr_cpu_ids; cpu++) {
222 is_valid = cpu_port_is_valid(&cpu_port[cpu]);
223 if (is_valid && cpu_port_match(&cpu_port[cpu], mpidr)) {
224 cci_port_control(cpu_port[cpu].port, false);
225 return 0;
226 }
227 }
228 return -ENODEV;
229}
230EXPORT_SYMBOL_GPL(cci_disable_port_by_cpu);
231
232/**
233 * __cci_control_port_by_device() - function to control a CCI port by device
234 * reference
235 *
236 * @dn: device node pointer of the device whose CCI port should be
237 * controlled
238 * @enable: if true enables the port, if false disables it
239 *
240 * Return:
241 * 0 on success
242 * -ENODEV on port look-up failure
243 */
244int notrace __cci_control_port_by_device(struct device_node *dn, bool enable)
245{
246 int port;
247
248 if (!dn)
249 return -ENODEV;
250
251 port = __cci_ace_get_port(dn, ACE_LITE_PORT);
252 if (WARN_ONCE(port < 0, "node %s ACE lite port look-up failure\n",
253 dn->full_name))
254 return -ENODEV;
255 cci_port_control(port, enable);
256 return 0;
257}
258EXPORT_SYMBOL_GPL(__cci_control_port_by_device);
259
260/**
261 * __cci_control_port_by_index() - function to control a CCI port by port index
262 *
263 * @port: port index previously retrieved with cci_ace_get_port()
264 * @enable: if true enables the port, if false disables it
265 *
266 * Return:
267 * 0 on success
268 * -ENODEV on port index out of range
269 * -EPERM if operation carried out on an ACE PORT
270 */
271int notrace __cci_control_port_by_index(u32 port, bool enable)
272{
273 if (port >= nb_cci_ports || ports[port].type == ACE_INVALID_PORT)
274 return -ENODEV;
275 /*
276 * CCI control for ports connected to CPUS is extremely fragile
277 * and must be made to go through a specific and controlled
278 * interface (ie cci_disable_port_by_cpu(); control by general purpose
279 * indexing is therefore disabled for ACE ports.
280 */
281 if (ports[port].type == ACE_PORT)
282 return -EPERM;
283
284 cci_port_control(port, enable);
285 return 0;
286}
287EXPORT_SYMBOL_GPL(__cci_control_port_by_index);
288
289static const struct cci_nb_ports cci400_ports = {
290 .nb_ace = 2,
291 .nb_ace_lite = 3
292};
293
294static const struct of_device_id arm_cci_matches[] = {
295 {.compatible = "arm,cci-400", .data = &cci400_ports },
296 {},
297};
298
299static const struct of_device_id arm_cci_ctrl_if_matches[] = {
300 {.compatible = "arm,cci-400-ctrl-if", },
301 {},
302};
303
304static int __init cci_probe(void)
305{
306 struct cci_nb_ports const *cci_config;
307 int ret, i, nb_ace = 0, nb_ace_lite = 0;
308 struct device_node *np, *cp;
309 const char *match_str;
310 bool is_ace;
311
312 np = of_find_matching_node(NULL, arm_cci_matches);
313 if (!np)
314 return -ENODEV;
315
316 cci_config = of_match_node(arm_cci_matches, np)->data;
317 if (!cci_config)
318 return -ENODEV;
319
320 nb_cci_ports = cci_config->nb_ace + cci_config->nb_ace_lite;
321
322 ports = kcalloc(sizeof(*ports), nb_cci_ports, GFP_KERNEL);
323 if (!ports)
324 return -ENOMEM;
325
326 cci_ctrl_base = of_iomap(np, 0);
327
328 if (!cci_ctrl_base) {
329 WARN(1, "unable to ioremap CCI ctrl\n");
330 ret = -ENXIO;
331 goto memalloc_err;
332 }
333
334 for_each_child_of_node(np, cp) {
335 if (!of_match_node(arm_cci_ctrl_if_matches, cp))
336 continue;
337
338 i = nb_ace + nb_ace_lite;
339
340 if (i >= nb_cci_ports)
341 break;
342
343 if (of_property_read_string(cp, "interface-type",
344 &match_str)) {
345 WARN(1, "node %s missing interface-type property\n",
346 cp->full_name);
347 continue;
348 }
349 is_ace = strcmp(match_str, "ace") == 0;
350 if (!is_ace && strcmp(match_str, "ace-lite")) {
351 WARN(1, "node %s containing invalid interface-type property, skipping it\n",
352 cp->full_name);
353 continue;
354 }
355
356 ports[i].base = of_iomap(cp, 0);
357
358 if (!ports[i].base) {
359 WARN(1, "unable to ioremap CCI port %d\n", i);
360 continue;
361 }
362
363 if (is_ace) {
364 if (WARN_ON(nb_ace >= cci_config->nb_ace))
365 continue;
366 ports[i].type = ACE_PORT;
367 ++nb_ace;
368 } else {
369 if (WARN_ON(nb_ace_lite >= cci_config->nb_ace_lite))
370 continue;
371 ports[i].type = ACE_LITE_PORT;
372 ++nb_ace_lite;
373 }
374 ports[i].dn = cp;
375 }
376
377 /* initialize a stashed array of ACE ports to speed-up look-up */
378 cci_ace_init_ports();
379
380 /*
381 * Multi-cluster systems may need this data when non-coherent, during
382 * cluster power-up/power-down. Make sure it reaches main memory.
383 */
384 sync_cache_w(&cci_ctrl_base);
385 sync_cache_w(&ports);
386 sync_cache_w(&cpu_port);
387 __sync_cache_range_w(ports, sizeof(*ports) * nb_cci_ports);
388 pr_info("ARM CCI driver probed\n");
389 return 0;
390
391memalloc_err:
392
393 kfree(ports);
394 return ret;
395}
396
397static int cci_init_status = -EAGAIN;
398static DEFINE_MUTEX(cci_probing);
399
400static int __init cci_init(void)
401{
402 if (cci_init_status != -EAGAIN)
403 return cci_init_status;
404
405 mutex_lock(&cci_probing);
406 if (cci_init_status == -EAGAIN)
407 cci_init_status = cci_probe();
408 mutex_unlock(&cci_probing);
409 return cci_init_status;
410}
411
412/*
413 * To sort out early init calls ordering a helper function is provided to
414 * check if the CCI driver has beed initialized. Function check if the driver
415 * has been initialized, if not it calls the init function that probes
416 * the driver and updates the return value.
417 */
418bool __init cci_probed(void)
419{
420 return cci_init() == 0;
421}
422EXPORT_SYMBOL_GPL(cci_probed);
423
424early_initcall(cci_init);
425MODULE_LICENSE("GPL");
426MODULE_DESCRIPTION("ARM CCI support");