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