xref: /openbmc/linux/arch/arm/mach-mvebu/coherency.c (revision d0de9323)
1009f1315SGregory CLEMENT /*
2009f1315SGregory CLEMENT  * Coherency fabric (Aurora) support for Armada 370 and XP platforms.
3009f1315SGregory CLEMENT  *
4009f1315SGregory CLEMENT  * Copyright (C) 2012 Marvell
5009f1315SGregory CLEMENT  *
6009f1315SGregory CLEMENT  * Yehuda Yitschak <yehuday@marvell.com>
7009f1315SGregory CLEMENT  * Gregory Clement <gregory.clement@free-electrons.com>
8009f1315SGregory CLEMENT  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
9009f1315SGregory CLEMENT  *
10009f1315SGregory CLEMENT  * This file is licensed under the terms of the GNU General Public
11009f1315SGregory CLEMENT  * License version 2.  This program is licensed "as is" without any
12009f1315SGregory CLEMENT  * warranty of any kind, whether express or implied.
13009f1315SGregory CLEMENT  *
14009f1315SGregory CLEMENT  * The Armada 370 and Armada XP SOCs have a coherency fabric which is
15009f1315SGregory CLEMENT  * responsible for ensuring hardware coherency between all CPUs and between
16009f1315SGregory CLEMENT  * CPUs and I/O masters. This file initializes the coherency fabric and
17009f1315SGregory CLEMENT  * supplies basic routines for configuring and controlling hardware coherency
18009f1315SGregory CLEMENT  */
19009f1315SGregory CLEMENT 
205ab5afd8SThomas Petazzoni #define pr_fmt(fmt) "mvebu-coherency: " fmt
215ab5afd8SThomas Petazzoni 
22009f1315SGregory CLEMENT #include <linux/kernel.h>
23009f1315SGregory CLEMENT #include <linux/init.h>
24009f1315SGregory CLEMENT #include <linux/of_address.h>
25009f1315SGregory CLEMENT #include <linux/io.h>
26009f1315SGregory CLEMENT #include <linux/smp.h>
27e60304f8SGregory CLEMENT #include <linux/dma-mapping.h>
28e60304f8SGregory CLEMENT #include <linux/platform_device.h>
295ab5afd8SThomas Petazzoni #include <linux/slab.h>
305ab5afd8SThomas Petazzoni #include <linux/mbus.h>
315ab5afd8SThomas Petazzoni #include <linux/clk.h>
32009f1315SGregory CLEMENT #include <asm/smp_plat.h>
33580ff0eeSThomas Petazzoni #include <asm/cacheflush.h>
34009f1315SGregory CLEMENT #include "armada-370-xp.h"
35b12634e3SJisheng Zhang #include "coherency.h"
36009f1315SGregory CLEMENT 
378bd26e3aSPaul Gortmaker unsigned long coherency_phys_base;
38865e0527SThomas Petazzoni static void __iomem *coherency_base;
39e60304f8SGregory CLEMENT static void __iomem *coherency_cpu_base;
40009f1315SGregory CLEMENT 
41009f1315SGregory CLEMENT /* Coherency fabric registers */
42009f1315SGregory CLEMENT #define COHERENCY_FABRIC_CFG_OFFSET		   0x4
43009f1315SGregory CLEMENT 
44e60304f8SGregory CLEMENT #define IO_SYNC_BARRIER_CTL_OFFSET		   0x0
45e60304f8SGregory CLEMENT 
46924d38f4SThomas Petazzoni enum {
47501f928eSThomas Petazzoni 	COHERENCY_FABRIC_TYPE_NONE,
48924d38f4SThomas Petazzoni 	COHERENCY_FABRIC_TYPE_ARMADA_370_XP,
4977fa4b9aSThomas Petazzoni 	COHERENCY_FABRIC_TYPE_ARMADA_375,
50d0de9323SThomas Petazzoni 	COHERENCY_FABRIC_TYPE_ARMADA_380,
51924d38f4SThomas Petazzoni };
52924d38f4SThomas Petazzoni 
53009f1315SGregory CLEMENT static struct of_device_id of_coherency_table[] = {
54924d38f4SThomas Petazzoni 	{.compatible = "marvell,coherency-fabric",
55924d38f4SThomas Petazzoni 	 .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_370_XP },
5677fa4b9aSThomas Petazzoni 	{.compatible = "marvell,armada-375-coherency-fabric",
5777fa4b9aSThomas Petazzoni 	 .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_375 },
58d0de9323SThomas Petazzoni 	{.compatible = "marvell,armada-380-coherency-fabric",
59d0de9323SThomas Petazzoni 	 .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_380 },
60009f1315SGregory CLEMENT 	{ /* end of list */ },
61009f1315SGregory CLEMENT };
62009f1315SGregory CLEMENT 
63009f1315SGregory CLEMENT /* Function defined in coherency_ll.S */
64009f1315SGregory CLEMENT int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id);
65009f1315SGregory CLEMENT 
66009f1315SGregory CLEMENT int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id)
67009f1315SGregory CLEMENT {
68009f1315SGregory CLEMENT 	if (!coherency_base) {
69009f1315SGregory CLEMENT 		pr_warn("Can't make CPU %d cache coherent.\n", hw_cpu_id);
70009f1315SGregory CLEMENT 		pr_warn("Coherency fabric is not initialized\n");
71009f1315SGregory CLEMENT 		return 1;
72009f1315SGregory CLEMENT 	}
73009f1315SGregory CLEMENT 
74009f1315SGregory CLEMENT 	return ll_set_cpu_coherent(coherency_base, hw_cpu_id);
75009f1315SGregory CLEMENT }
76009f1315SGregory CLEMENT 
775ab5afd8SThomas Petazzoni /*
785ab5afd8SThomas Petazzoni  * The below code implements the I/O coherency workaround on Armada
795ab5afd8SThomas Petazzoni  * 375. This workaround consists in using the two channels of the
805ab5afd8SThomas Petazzoni  * first XOR engine to trigger a XOR transaction that serves as the
815ab5afd8SThomas Petazzoni  * I/O coherency barrier.
825ab5afd8SThomas Petazzoni  */
835ab5afd8SThomas Petazzoni 
845ab5afd8SThomas Petazzoni static void __iomem *xor_base, *xor_high_base;
855ab5afd8SThomas Petazzoni static dma_addr_t coherency_wa_buf_phys[CONFIG_NR_CPUS];
865ab5afd8SThomas Petazzoni static void *coherency_wa_buf[CONFIG_NR_CPUS];
875ab5afd8SThomas Petazzoni static bool coherency_wa_enabled;
885ab5afd8SThomas Petazzoni 
895ab5afd8SThomas Petazzoni #define XOR_CONFIG(chan)            (0x10 + (chan * 4))
905ab5afd8SThomas Petazzoni #define XOR_ACTIVATION(chan)        (0x20 + (chan * 4))
915ab5afd8SThomas Petazzoni #define WINDOW_BAR_ENABLE(chan)     (0x240 + ((chan) << 2))
925ab5afd8SThomas Petazzoni #define WINDOW_BASE(w)              (0x250 + ((w) << 2))
935ab5afd8SThomas Petazzoni #define WINDOW_SIZE(w)              (0x270 + ((w) << 2))
945ab5afd8SThomas Petazzoni #define WINDOW_REMAP_HIGH(w)        (0x290 + ((w) << 2))
955ab5afd8SThomas Petazzoni #define WINDOW_OVERRIDE_CTRL(chan)  (0x2A0 + ((chan) << 2))
965ab5afd8SThomas Petazzoni #define XOR_DEST_POINTER(chan)      (0x2B0 + (chan * 4))
975ab5afd8SThomas Petazzoni #define XOR_BLOCK_SIZE(chan)        (0x2C0 + (chan * 4))
985ab5afd8SThomas Petazzoni #define XOR_INIT_VALUE_LOW           0x2E0
995ab5afd8SThomas Petazzoni #define XOR_INIT_VALUE_HIGH          0x2E4
1005ab5afd8SThomas Petazzoni 
1015ab5afd8SThomas Petazzoni static inline void mvebu_hwcc_armada375_sync_io_barrier_wa(void)
1025ab5afd8SThomas Petazzoni {
1035ab5afd8SThomas Petazzoni 	int idx = smp_processor_id();
1045ab5afd8SThomas Petazzoni 
1055ab5afd8SThomas Petazzoni 	/* Write '1' to the first word of the buffer */
1065ab5afd8SThomas Petazzoni 	writel(0x1, coherency_wa_buf[idx]);
1075ab5afd8SThomas Petazzoni 
1085ab5afd8SThomas Petazzoni 	/* Wait until the engine is idle */
1095ab5afd8SThomas Petazzoni 	while ((readl(xor_base + XOR_ACTIVATION(idx)) >> 4) & 0x3)
1105ab5afd8SThomas Petazzoni 		;
1115ab5afd8SThomas Petazzoni 
1125ab5afd8SThomas Petazzoni 	dmb();
1135ab5afd8SThomas Petazzoni 
1145ab5afd8SThomas Petazzoni 	/* Trigger channel */
1155ab5afd8SThomas Petazzoni 	writel(0x1, xor_base + XOR_ACTIVATION(idx));
1165ab5afd8SThomas Petazzoni 
1175ab5afd8SThomas Petazzoni 	/* Poll the data until it is cleared by the XOR transaction */
1185ab5afd8SThomas Petazzoni 	while (readl(coherency_wa_buf[idx]))
1195ab5afd8SThomas Petazzoni 		;
1205ab5afd8SThomas Petazzoni }
1215ab5afd8SThomas Petazzoni 
1225ab5afd8SThomas Petazzoni static void __init armada_375_coherency_init_wa(void)
1235ab5afd8SThomas Petazzoni {
1245ab5afd8SThomas Petazzoni 	const struct mbus_dram_target_info *dram;
1255ab5afd8SThomas Petazzoni 	struct device_node *xor_node;
1265ab5afd8SThomas Petazzoni 	struct property *xor_status;
1275ab5afd8SThomas Petazzoni 	struct clk *xor_clk;
1285ab5afd8SThomas Petazzoni 	u32 win_enable = 0;
1295ab5afd8SThomas Petazzoni 	int i;
1305ab5afd8SThomas Petazzoni 
1315ab5afd8SThomas Petazzoni 	pr_warn("enabling coherency workaround for Armada 375 Z1, one XOR engine disabled\n");
1325ab5afd8SThomas Petazzoni 
1335ab5afd8SThomas Petazzoni 	/*
1345ab5afd8SThomas Petazzoni 	 * Since the workaround uses one XOR engine, we grab a
1355ab5afd8SThomas Petazzoni 	 * reference to its Device Tree node first.
1365ab5afd8SThomas Petazzoni 	 */
1375ab5afd8SThomas Petazzoni 	xor_node = of_find_compatible_node(NULL, NULL, "marvell,orion-xor");
1385ab5afd8SThomas Petazzoni 	BUG_ON(!xor_node);
1395ab5afd8SThomas Petazzoni 
1405ab5afd8SThomas Petazzoni 	/*
1415ab5afd8SThomas Petazzoni 	 * Then we mark it as disabled so that the real XOR driver
1425ab5afd8SThomas Petazzoni 	 * will not use it.
1435ab5afd8SThomas Petazzoni 	 */
1445ab5afd8SThomas Petazzoni 	xor_status = kzalloc(sizeof(struct property), GFP_KERNEL);
1455ab5afd8SThomas Petazzoni 	BUG_ON(!xor_status);
1465ab5afd8SThomas Petazzoni 
1475ab5afd8SThomas Petazzoni 	xor_status->value = kstrdup("disabled", GFP_KERNEL);
1485ab5afd8SThomas Petazzoni 	BUG_ON(!xor_status->value);
1495ab5afd8SThomas Petazzoni 
1505ab5afd8SThomas Petazzoni 	xor_status->length = 8;
1515ab5afd8SThomas Petazzoni 	xor_status->name = kstrdup("status", GFP_KERNEL);
1525ab5afd8SThomas Petazzoni 	BUG_ON(!xor_status->name);
1535ab5afd8SThomas Petazzoni 
1545ab5afd8SThomas Petazzoni 	of_update_property(xor_node, xor_status);
1555ab5afd8SThomas Petazzoni 
1565ab5afd8SThomas Petazzoni 	/*
1575ab5afd8SThomas Petazzoni 	 * And we remap the registers, get the clock, and do the
1585ab5afd8SThomas Petazzoni 	 * initial configuration of the XOR engine.
1595ab5afd8SThomas Petazzoni 	 */
1605ab5afd8SThomas Petazzoni 	xor_base = of_iomap(xor_node, 0);
1615ab5afd8SThomas Petazzoni 	xor_high_base = of_iomap(xor_node, 1);
1625ab5afd8SThomas Petazzoni 
1635ab5afd8SThomas Petazzoni 	xor_clk = of_clk_get_by_name(xor_node, NULL);
1645ab5afd8SThomas Petazzoni 	BUG_ON(!xor_clk);
1655ab5afd8SThomas Petazzoni 
1665ab5afd8SThomas Petazzoni 	clk_prepare_enable(xor_clk);
1675ab5afd8SThomas Petazzoni 
1685ab5afd8SThomas Petazzoni 	dram = mv_mbus_dram_info();
1695ab5afd8SThomas Petazzoni 
1705ab5afd8SThomas Petazzoni 	for (i = 0; i < 8; i++) {
1715ab5afd8SThomas Petazzoni 		writel(0, xor_base + WINDOW_BASE(i));
1725ab5afd8SThomas Petazzoni 		writel(0, xor_base + WINDOW_SIZE(i));
1735ab5afd8SThomas Petazzoni 		if (i < 4)
1745ab5afd8SThomas Petazzoni 			writel(0, xor_base + WINDOW_REMAP_HIGH(i));
1755ab5afd8SThomas Petazzoni 	}
1765ab5afd8SThomas Petazzoni 
1775ab5afd8SThomas Petazzoni 	for (i = 0; i < dram->num_cs; i++) {
1785ab5afd8SThomas Petazzoni 		const struct mbus_dram_window *cs = dram->cs + i;
1795ab5afd8SThomas Petazzoni 		writel((cs->base & 0xffff0000) |
1805ab5afd8SThomas Petazzoni 		       (cs->mbus_attr << 8) |
1815ab5afd8SThomas Petazzoni 		       dram->mbus_dram_target_id, xor_base + WINDOW_BASE(i));
1825ab5afd8SThomas Petazzoni 		writel((cs->size - 1) & 0xffff0000, xor_base + WINDOW_SIZE(i));
1835ab5afd8SThomas Petazzoni 
1845ab5afd8SThomas Petazzoni 		win_enable |= (1 << i);
1855ab5afd8SThomas Petazzoni 		win_enable |= 3 << (16 + (2 * i));
1865ab5afd8SThomas Petazzoni 	}
1875ab5afd8SThomas Petazzoni 
1885ab5afd8SThomas Petazzoni 	writel(win_enable, xor_base + WINDOW_BAR_ENABLE(0));
1895ab5afd8SThomas Petazzoni 	writel(win_enable, xor_base + WINDOW_BAR_ENABLE(1));
1905ab5afd8SThomas Petazzoni 	writel(0, xor_base + WINDOW_OVERRIDE_CTRL(0));
1915ab5afd8SThomas Petazzoni 	writel(0, xor_base + WINDOW_OVERRIDE_CTRL(1));
1925ab5afd8SThomas Petazzoni 
1935ab5afd8SThomas Petazzoni 	for (i = 0; i < CONFIG_NR_CPUS; i++) {
1945ab5afd8SThomas Petazzoni 		coherency_wa_buf[i] = kzalloc(PAGE_SIZE, GFP_KERNEL);
1955ab5afd8SThomas Petazzoni 		BUG_ON(!coherency_wa_buf[i]);
1965ab5afd8SThomas Petazzoni 
1975ab5afd8SThomas Petazzoni 		/*
1985ab5afd8SThomas Petazzoni 		 * We can't use the DMA mapping API, since we don't
1995ab5afd8SThomas Petazzoni 		 * have a valid 'struct device' pointer
2005ab5afd8SThomas Petazzoni 		 */
2015ab5afd8SThomas Petazzoni 		coherency_wa_buf_phys[i] =
2025ab5afd8SThomas Petazzoni 			virt_to_phys(coherency_wa_buf[i]);
2035ab5afd8SThomas Petazzoni 		BUG_ON(!coherency_wa_buf_phys[i]);
2045ab5afd8SThomas Petazzoni 
2055ab5afd8SThomas Petazzoni 		/*
2065ab5afd8SThomas Petazzoni 		 * Configure the XOR engine for memset operation, with
2075ab5afd8SThomas Petazzoni 		 * a 128 bytes block size
2085ab5afd8SThomas Petazzoni 		 */
2095ab5afd8SThomas Petazzoni 		writel(0x444, xor_base + XOR_CONFIG(i));
2105ab5afd8SThomas Petazzoni 		writel(128, xor_base + XOR_BLOCK_SIZE(i));
2115ab5afd8SThomas Petazzoni 		writel(coherency_wa_buf_phys[i],
2125ab5afd8SThomas Petazzoni 		       xor_base + XOR_DEST_POINTER(i));
2135ab5afd8SThomas Petazzoni 	}
2145ab5afd8SThomas Petazzoni 
2155ab5afd8SThomas Petazzoni 	writel(0x0, xor_base + XOR_INIT_VALUE_LOW);
2165ab5afd8SThomas Petazzoni 	writel(0x0, xor_base + XOR_INIT_VALUE_HIGH);
2175ab5afd8SThomas Petazzoni 
2185ab5afd8SThomas Petazzoni 	coherency_wa_enabled = true;
2195ab5afd8SThomas Petazzoni }
2205ab5afd8SThomas Petazzoni 
221e60304f8SGregory CLEMENT static inline void mvebu_hwcc_sync_io_barrier(void)
222e60304f8SGregory CLEMENT {
2235ab5afd8SThomas Petazzoni 	if (coherency_wa_enabled) {
2245ab5afd8SThomas Petazzoni 		mvebu_hwcc_armada375_sync_io_barrier_wa();
2255ab5afd8SThomas Petazzoni 		return;
2265ab5afd8SThomas Petazzoni 	}
2275ab5afd8SThomas Petazzoni 
228e60304f8SGregory CLEMENT 	writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET);
229e60304f8SGregory CLEMENT 	while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1);
230e60304f8SGregory CLEMENT }
231e60304f8SGregory CLEMENT 
232e60304f8SGregory CLEMENT static dma_addr_t mvebu_hwcc_dma_map_page(struct device *dev, struct page *page,
233e60304f8SGregory CLEMENT 				  unsigned long offset, size_t size,
234e60304f8SGregory CLEMENT 				  enum dma_data_direction dir,
235e60304f8SGregory CLEMENT 				  struct dma_attrs *attrs)
236e60304f8SGregory CLEMENT {
237e60304f8SGregory CLEMENT 	if (dir != DMA_TO_DEVICE)
238e60304f8SGregory CLEMENT 		mvebu_hwcc_sync_io_barrier();
239e60304f8SGregory CLEMENT 	return pfn_to_dma(dev, page_to_pfn(page)) + offset;
240e60304f8SGregory CLEMENT }
241e60304f8SGregory CLEMENT 
242e60304f8SGregory CLEMENT 
243e60304f8SGregory CLEMENT static void mvebu_hwcc_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
244e60304f8SGregory CLEMENT 			      size_t size, enum dma_data_direction dir,
245e60304f8SGregory CLEMENT 			      struct dma_attrs *attrs)
246e60304f8SGregory CLEMENT {
247e60304f8SGregory CLEMENT 	if (dir != DMA_TO_DEVICE)
248e60304f8SGregory CLEMENT 		mvebu_hwcc_sync_io_barrier();
249e60304f8SGregory CLEMENT }
250e60304f8SGregory CLEMENT 
251e60304f8SGregory CLEMENT static void mvebu_hwcc_dma_sync(struct device *dev, dma_addr_t dma_handle,
252e60304f8SGregory CLEMENT 			size_t size, enum dma_data_direction dir)
253e60304f8SGregory CLEMENT {
254e60304f8SGregory CLEMENT 	if (dir != DMA_TO_DEVICE)
255e60304f8SGregory CLEMENT 		mvebu_hwcc_sync_io_barrier();
256e60304f8SGregory CLEMENT }
257e60304f8SGregory CLEMENT 
258e60304f8SGregory CLEMENT static struct dma_map_ops mvebu_hwcc_dma_ops = {
259e60304f8SGregory CLEMENT 	.alloc			= arm_dma_alloc,
260e60304f8SGregory CLEMENT 	.free			= arm_dma_free,
261e60304f8SGregory CLEMENT 	.mmap			= arm_dma_mmap,
262e60304f8SGregory CLEMENT 	.map_page		= mvebu_hwcc_dma_map_page,
263e60304f8SGregory CLEMENT 	.unmap_page		= mvebu_hwcc_dma_unmap_page,
264e60304f8SGregory CLEMENT 	.get_sgtable		= arm_dma_get_sgtable,
265e60304f8SGregory CLEMENT 	.map_sg			= arm_dma_map_sg,
266e60304f8SGregory CLEMENT 	.unmap_sg		= arm_dma_unmap_sg,
267e60304f8SGregory CLEMENT 	.sync_single_for_cpu	= mvebu_hwcc_dma_sync,
268e60304f8SGregory CLEMENT 	.sync_single_for_device	= mvebu_hwcc_dma_sync,
269e60304f8SGregory CLEMENT 	.sync_sg_for_cpu	= arm_dma_sync_sg_for_cpu,
270e60304f8SGregory CLEMENT 	.sync_sg_for_device	= arm_dma_sync_sg_for_device,
271e60304f8SGregory CLEMENT 	.set_dma_mask		= arm_dma_set_mask,
272e60304f8SGregory CLEMENT };
273e60304f8SGregory CLEMENT 
274e60304f8SGregory CLEMENT static int mvebu_hwcc_platform_notifier(struct notifier_block *nb,
275e60304f8SGregory CLEMENT 				       unsigned long event, void *__dev)
276e60304f8SGregory CLEMENT {
277e60304f8SGregory CLEMENT 	struct device *dev = __dev;
278e60304f8SGregory CLEMENT 
279e60304f8SGregory CLEMENT 	if (event != BUS_NOTIFY_ADD_DEVICE)
280e60304f8SGregory CLEMENT 		return NOTIFY_DONE;
281e60304f8SGregory CLEMENT 	set_dma_ops(dev, &mvebu_hwcc_dma_ops);
282e60304f8SGregory CLEMENT 
283e60304f8SGregory CLEMENT 	return NOTIFY_OK;
284e60304f8SGregory CLEMENT }
285e60304f8SGregory CLEMENT 
286e60304f8SGregory CLEMENT static struct notifier_block mvebu_hwcc_platform_nb = {
287e60304f8SGregory CLEMENT 	.notifier_call = mvebu_hwcc_platform_notifier,
288e60304f8SGregory CLEMENT };
289e60304f8SGregory CLEMENT 
290924d38f4SThomas Petazzoni static void __init armada_370_coherency_init(struct device_node *np)
291009f1315SGregory CLEMENT {
292580ff0eeSThomas Petazzoni 	struct resource res;
293924d38f4SThomas Petazzoni 
294580ff0eeSThomas Petazzoni 	of_address_to_resource(np, 0, &res);
295580ff0eeSThomas Petazzoni 	coherency_phys_base = res.start;
296580ff0eeSThomas Petazzoni 	/*
297580ff0eeSThomas Petazzoni 	 * Ensure secondary CPUs will see the updated value,
298580ff0eeSThomas Petazzoni 	 * which they read before they join the coherency
299580ff0eeSThomas Petazzoni 	 * fabric, and therefore before they are coherent with
300580ff0eeSThomas Petazzoni 	 * the boot CPU cache.
301580ff0eeSThomas Petazzoni 	 */
302580ff0eeSThomas Petazzoni 	sync_cache_w(&coherency_phys_base);
303009f1315SGregory CLEMENT 	coherency_base = of_iomap(np, 0);
304e60304f8SGregory CLEMENT 	coherency_cpu_base = of_iomap(np, 1);
305e60304f8SGregory CLEMENT 	set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0);
306924d38f4SThomas Petazzoni }
307924d38f4SThomas Petazzoni 
308d0de9323SThomas Petazzoni static void __init armada_375_380_coherency_init(struct device_node *np)
30977fa4b9aSThomas Petazzoni {
31077fa4b9aSThomas Petazzoni 	coherency_cpu_base = of_iomap(np, 0);
31177fa4b9aSThomas Petazzoni }
31277fa4b9aSThomas Petazzoni 
313501f928eSThomas Petazzoni static int coherency_type(void)
314924d38f4SThomas Petazzoni {
315924d38f4SThomas Petazzoni 	struct device_node *np;
3165fbba080SThomas Petazzoni 	const struct of_device_id *match;
317924d38f4SThomas Petazzoni 
3185fbba080SThomas Petazzoni 	np = of_find_matching_node_and_match(NULL, of_coherency_table, &match);
319924d38f4SThomas Petazzoni 	if (np) {
3205fbba080SThomas Petazzoni 		int type = (int) match->data;
321924d38f4SThomas Petazzoni 
322501f928eSThomas Petazzoni 		/* Armada 370/XP coherency works in both UP and SMP */
323924d38f4SThomas Petazzoni 		if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP)
324501f928eSThomas Petazzoni 			return type;
325924d38f4SThomas Petazzoni 
32677fa4b9aSThomas Petazzoni 		/* Armada 375 coherency works only on SMP */
32777fa4b9aSThomas Petazzoni 		else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 && is_smp())
32877fa4b9aSThomas Petazzoni 			return type;
32977fa4b9aSThomas Petazzoni 
330d0de9323SThomas Petazzoni 		/* Armada 380 coherency works only on SMP */
331d0de9323SThomas Petazzoni 		else if (type == COHERENCY_FABRIC_TYPE_ARMADA_380 && is_smp())
332d0de9323SThomas Petazzoni 			return type;
333d0de9323SThomas Petazzoni 
334abe511acSJisheng Zhang 		of_node_put(np);
335009f1315SGregory CLEMENT 	}
336009f1315SGregory CLEMENT 
337501f928eSThomas Petazzoni 	return COHERENCY_FABRIC_TYPE_NONE;
338501f928eSThomas Petazzoni }
339501f928eSThomas Petazzoni 
340501f928eSThomas Petazzoni int coherency_available(void)
341501f928eSThomas Petazzoni {
342501f928eSThomas Petazzoni 	return coherency_type() != COHERENCY_FABRIC_TYPE_NONE;
343501f928eSThomas Petazzoni }
344501f928eSThomas Petazzoni 
345501f928eSThomas Petazzoni int __init coherency_init(void)
346501f928eSThomas Petazzoni {
347501f928eSThomas Petazzoni 	int type = coherency_type();
348501f928eSThomas Petazzoni 	struct device_node *np;
349501f928eSThomas Petazzoni 
350501f928eSThomas Petazzoni 	np = of_find_matching_node(NULL, of_coherency_table);
351501f928eSThomas Petazzoni 
352501f928eSThomas Petazzoni 	if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP)
353501f928eSThomas Petazzoni 		armada_370_coherency_init(np);
354d0de9323SThomas Petazzoni 	else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 ||
355d0de9323SThomas Petazzoni 		 type == COHERENCY_FABRIC_TYPE_ARMADA_380)
356d0de9323SThomas Petazzoni 		armada_375_380_coherency_init(np);
357501f928eSThomas Petazzoni 
358009f1315SGregory CLEMENT 	return 0;
359009f1315SGregory CLEMENT }
360865e0527SThomas Petazzoni 
361865e0527SThomas Petazzoni static int __init coherency_late_init(void)
362865e0527SThomas Petazzoni {
3635ab5afd8SThomas Petazzoni 	int type = coherency_type();
3645ab5afd8SThomas Petazzoni 
3655ab5afd8SThomas Petazzoni 	if (type == COHERENCY_FABRIC_TYPE_NONE)
3665ab5afd8SThomas Petazzoni 		return 0;
3675ab5afd8SThomas Petazzoni 
3685ab5afd8SThomas Petazzoni 	if (type == COHERENCY_FABRIC_TYPE_ARMADA_375)
3695ab5afd8SThomas Petazzoni 		armada_375_coherency_init_wa();
3705ab5afd8SThomas Petazzoni 
371865e0527SThomas Petazzoni 	bus_register_notifier(&platform_bus_type,
372865e0527SThomas Petazzoni 			      &mvebu_hwcc_platform_nb);
3735ab5afd8SThomas Petazzoni 
374865e0527SThomas Petazzoni 	return 0;
375865e0527SThomas Petazzoni }
376865e0527SThomas Petazzoni 
377865e0527SThomas Petazzoni postcore_initcall(coherency_late_init);
378