xref: /openbmc/linux/arch/arm/mach-mvebu/coherency.c (revision 39438567)
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"
3639438567SThomas Petazzoni #include "mvebu-soc-id.h"
37009f1315SGregory CLEMENT 
388bd26e3aSPaul Gortmaker unsigned long coherency_phys_base;
39ccd6a131SGregory CLEMENT void __iomem *coherency_base;
40e60304f8SGregory CLEMENT static void __iomem *coherency_cpu_base;
41009f1315SGregory CLEMENT 
42009f1315SGregory CLEMENT /* Coherency fabric registers */
43009f1315SGregory CLEMENT #define COHERENCY_FABRIC_CFG_OFFSET		   0x4
44009f1315SGregory CLEMENT 
45e60304f8SGregory CLEMENT #define IO_SYNC_BARRIER_CTL_OFFSET		   0x0
46e60304f8SGregory CLEMENT 
47924d38f4SThomas Petazzoni enum {
48501f928eSThomas Petazzoni 	COHERENCY_FABRIC_TYPE_NONE,
49924d38f4SThomas Petazzoni 	COHERENCY_FABRIC_TYPE_ARMADA_370_XP,
5077fa4b9aSThomas Petazzoni 	COHERENCY_FABRIC_TYPE_ARMADA_375,
51d0de9323SThomas Petazzoni 	COHERENCY_FABRIC_TYPE_ARMADA_380,
52924d38f4SThomas Petazzoni };
53924d38f4SThomas Petazzoni 
54009f1315SGregory CLEMENT static struct of_device_id of_coherency_table[] = {
55924d38f4SThomas Petazzoni 	{.compatible = "marvell,coherency-fabric",
56924d38f4SThomas Petazzoni 	 .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_370_XP },
5777fa4b9aSThomas Petazzoni 	{.compatible = "marvell,armada-375-coherency-fabric",
5877fa4b9aSThomas Petazzoni 	 .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_375 },
59d0de9323SThomas Petazzoni 	{.compatible = "marvell,armada-380-coherency-fabric",
60d0de9323SThomas Petazzoni 	 .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_380 },
61009f1315SGregory CLEMENT 	{ /* end of list */ },
62009f1315SGregory CLEMENT };
63009f1315SGregory CLEMENT 
642e8a5942SGregory CLEMENT /* Functions defined in coherency_ll.S */
652e8a5942SGregory CLEMENT int ll_enable_coherency(void);
662e8a5942SGregory CLEMENT void ll_add_cpu_to_smp_group(void);
67009f1315SGregory CLEMENT 
68952f4ca7SGregory CLEMENT int set_cpu_coherent(void)
69009f1315SGregory CLEMENT {
70009f1315SGregory CLEMENT 	if (!coherency_base) {
71b41375f7SGregory CLEMENT 		pr_warn("Can't make current CPU cache coherent.\n");
72009f1315SGregory CLEMENT 		pr_warn("Coherency fabric is not initialized\n");
73009f1315SGregory CLEMENT 		return 1;
74009f1315SGregory CLEMENT 	}
75009f1315SGregory CLEMENT 
762e8a5942SGregory CLEMENT 	ll_add_cpu_to_smp_group();
772e8a5942SGregory CLEMENT 	return ll_enable_coherency();
78009f1315SGregory CLEMENT }
79009f1315SGregory CLEMENT 
805ab5afd8SThomas Petazzoni /*
815ab5afd8SThomas Petazzoni  * The below code implements the I/O coherency workaround on Armada
825ab5afd8SThomas Petazzoni  * 375. This workaround consists in using the two channels of the
835ab5afd8SThomas Petazzoni  * first XOR engine to trigger a XOR transaction that serves as the
845ab5afd8SThomas Petazzoni  * I/O coherency barrier.
855ab5afd8SThomas Petazzoni  */
865ab5afd8SThomas Petazzoni 
875ab5afd8SThomas Petazzoni static void __iomem *xor_base, *xor_high_base;
885ab5afd8SThomas Petazzoni static dma_addr_t coherency_wa_buf_phys[CONFIG_NR_CPUS];
895ab5afd8SThomas Petazzoni static void *coherency_wa_buf[CONFIG_NR_CPUS];
905ab5afd8SThomas Petazzoni static bool coherency_wa_enabled;
915ab5afd8SThomas Petazzoni 
925ab5afd8SThomas Petazzoni #define XOR_CONFIG(chan)            (0x10 + (chan * 4))
935ab5afd8SThomas Petazzoni #define XOR_ACTIVATION(chan)        (0x20 + (chan * 4))
945ab5afd8SThomas Petazzoni #define WINDOW_BAR_ENABLE(chan)     (0x240 + ((chan) << 2))
955ab5afd8SThomas Petazzoni #define WINDOW_BASE(w)              (0x250 + ((w) << 2))
965ab5afd8SThomas Petazzoni #define WINDOW_SIZE(w)              (0x270 + ((w) << 2))
975ab5afd8SThomas Petazzoni #define WINDOW_REMAP_HIGH(w)        (0x290 + ((w) << 2))
985ab5afd8SThomas Petazzoni #define WINDOW_OVERRIDE_CTRL(chan)  (0x2A0 + ((chan) << 2))
995ab5afd8SThomas Petazzoni #define XOR_DEST_POINTER(chan)      (0x2B0 + (chan * 4))
1005ab5afd8SThomas Petazzoni #define XOR_BLOCK_SIZE(chan)        (0x2C0 + (chan * 4))
1015ab5afd8SThomas Petazzoni #define XOR_INIT_VALUE_LOW           0x2E0
1025ab5afd8SThomas Petazzoni #define XOR_INIT_VALUE_HIGH          0x2E4
1035ab5afd8SThomas Petazzoni 
1045ab5afd8SThomas Petazzoni static inline void mvebu_hwcc_armada375_sync_io_barrier_wa(void)
1055ab5afd8SThomas Petazzoni {
1065ab5afd8SThomas Petazzoni 	int idx = smp_processor_id();
1075ab5afd8SThomas Petazzoni 
1085ab5afd8SThomas Petazzoni 	/* Write '1' to the first word of the buffer */
1095ab5afd8SThomas Petazzoni 	writel(0x1, coherency_wa_buf[idx]);
1105ab5afd8SThomas Petazzoni 
1115ab5afd8SThomas Petazzoni 	/* Wait until the engine is idle */
1125ab5afd8SThomas Petazzoni 	while ((readl(xor_base + XOR_ACTIVATION(idx)) >> 4) & 0x3)
1135ab5afd8SThomas Petazzoni 		;
1145ab5afd8SThomas Petazzoni 
1155ab5afd8SThomas Petazzoni 	dmb();
1165ab5afd8SThomas Petazzoni 
1175ab5afd8SThomas Petazzoni 	/* Trigger channel */
1185ab5afd8SThomas Petazzoni 	writel(0x1, xor_base + XOR_ACTIVATION(idx));
1195ab5afd8SThomas Petazzoni 
1205ab5afd8SThomas Petazzoni 	/* Poll the data until it is cleared by the XOR transaction */
1215ab5afd8SThomas Petazzoni 	while (readl(coherency_wa_buf[idx]))
1225ab5afd8SThomas Petazzoni 		;
1235ab5afd8SThomas Petazzoni }
1245ab5afd8SThomas Petazzoni 
1255ab5afd8SThomas Petazzoni static void __init armada_375_coherency_init_wa(void)
1265ab5afd8SThomas Petazzoni {
1275ab5afd8SThomas Petazzoni 	const struct mbus_dram_target_info *dram;
1285ab5afd8SThomas Petazzoni 	struct device_node *xor_node;
1295ab5afd8SThomas Petazzoni 	struct property *xor_status;
1305ab5afd8SThomas Petazzoni 	struct clk *xor_clk;
1315ab5afd8SThomas Petazzoni 	u32 win_enable = 0;
1325ab5afd8SThomas Petazzoni 	int i;
1335ab5afd8SThomas Petazzoni 
1345ab5afd8SThomas Petazzoni 	pr_warn("enabling coherency workaround for Armada 375 Z1, one XOR engine disabled\n");
1355ab5afd8SThomas Petazzoni 
1365ab5afd8SThomas Petazzoni 	/*
1375ab5afd8SThomas Petazzoni 	 * Since the workaround uses one XOR engine, we grab a
1385ab5afd8SThomas Petazzoni 	 * reference to its Device Tree node first.
1395ab5afd8SThomas Petazzoni 	 */
1405ab5afd8SThomas Petazzoni 	xor_node = of_find_compatible_node(NULL, NULL, "marvell,orion-xor");
1415ab5afd8SThomas Petazzoni 	BUG_ON(!xor_node);
1425ab5afd8SThomas Petazzoni 
1435ab5afd8SThomas Petazzoni 	/*
1445ab5afd8SThomas Petazzoni 	 * Then we mark it as disabled so that the real XOR driver
1455ab5afd8SThomas Petazzoni 	 * will not use it.
1465ab5afd8SThomas Petazzoni 	 */
1475ab5afd8SThomas Petazzoni 	xor_status = kzalloc(sizeof(struct property), GFP_KERNEL);
1485ab5afd8SThomas Petazzoni 	BUG_ON(!xor_status);
1495ab5afd8SThomas Petazzoni 
1505ab5afd8SThomas Petazzoni 	xor_status->value = kstrdup("disabled", GFP_KERNEL);
1515ab5afd8SThomas Petazzoni 	BUG_ON(!xor_status->value);
1525ab5afd8SThomas Petazzoni 
1535ab5afd8SThomas Petazzoni 	xor_status->length = 8;
1545ab5afd8SThomas Petazzoni 	xor_status->name = kstrdup("status", GFP_KERNEL);
1555ab5afd8SThomas Petazzoni 	BUG_ON(!xor_status->name);
1565ab5afd8SThomas Petazzoni 
1575ab5afd8SThomas Petazzoni 	of_update_property(xor_node, xor_status);
1585ab5afd8SThomas Petazzoni 
1595ab5afd8SThomas Petazzoni 	/*
1605ab5afd8SThomas Petazzoni 	 * And we remap the registers, get the clock, and do the
1615ab5afd8SThomas Petazzoni 	 * initial configuration of the XOR engine.
1625ab5afd8SThomas Petazzoni 	 */
1635ab5afd8SThomas Petazzoni 	xor_base = of_iomap(xor_node, 0);
1645ab5afd8SThomas Petazzoni 	xor_high_base = of_iomap(xor_node, 1);
1655ab5afd8SThomas Petazzoni 
1665ab5afd8SThomas Petazzoni 	xor_clk = of_clk_get_by_name(xor_node, NULL);
1675ab5afd8SThomas Petazzoni 	BUG_ON(!xor_clk);
1685ab5afd8SThomas Petazzoni 
1695ab5afd8SThomas Petazzoni 	clk_prepare_enable(xor_clk);
1705ab5afd8SThomas Petazzoni 
1715ab5afd8SThomas Petazzoni 	dram = mv_mbus_dram_info();
1725ab5afd8SThomas Petazzoni 
1735ab5afd8SThomas Petazzoni 	for (i = 0; i < 8; i++) {
1745ab5afd8SThomas Petazzoni 		writel(0, xor_base + WINDOW_BASE(i));
1755ab5afd8SThomas Petazzoni 		writel(0, xor_base + WINDOW_SIZE(i));
1765ab5afd8SThomas Petazzoni 		if (i < 4)
1775ab5afd8SThomas Petazzoni 			writel(0, xor_base + WINDOW_REMAP_HIGH(i));
1785ab5afd8SThomas Petazzoni 	}
1795ab5afd8SThomas Petazzoni 
1805ab5afd8SThomas Petazzoni 	for (i = 0; i < dram->num_cs; i++) {
1815ab5afd8SThomas Petazzoni 		const struct mbus_dram_window *cs = dram->cs + i;
1825ab5afd8SThomas Petazzoni 		writel((cs->base & 0xffff0000) |
1835ab5afd8SThomas Petazzoni 		       (cs->mbus_attr << 8) |
1845ab5afd8SThomas Petazzoni 		       dram->mbus_dram_target_id, xor_base + WINDOW_BASE(i));
1855ab5afd8SThomas Petazzoni 		writel((cs->size - 1) & 0xffff0000, xor_base + WINDOW_SIZE(i));
1865ab5afd8SThomas Petazzoni 
1875ab5afd8SThomas Petazzoni 		win_enable |= (1 << i);
1885ab5afd8SThomas Petazzoni 		win_enable |= 3 << (16 + (2 * i));
1895ab5afd8SThomas Petazzoni 	}
1905ab5afd8SThomas Petazzoni 
1915ab5afd8SThomas Petazzoni 	writel(win_enable, xor_base + WINDOW_BAR_ENABLE(0));
1925ab5afd8SThomas Petazzoni 	writel(win_enable, xor_base + WINDOW_BAR_ENABLE(1));
1935ab5afd8SThomas Petazzoni 	writel(0, xor_base + WINDOW_OVERRIDE_CTRL(0));
1945ab5afd8SThomas Petazzoni 	writel(0, xor_base + WINDOW_OVERRIDE_CTRL(1));
1955ab5afd8SThomas Petazzoni 
1965ab5afd8SThomas Petazzoni 	for (i = 0; i < CONFIG_NR_CPUS; i++) {
1975ab5afd8SThomas Petazzoni 		coherency_wa_buf[i] = kzalloc(PAGE_SIZE, GFP_KERNEL);
1985ab5afd8SThomas Petazzoni 		BUG_ON(!coherency_wa_buf[i]);
1995ab5afd8SThomas Petazzoni 
2005ab5afd8SThomas Petazzoni 		/*
2015ab5afd8SThomas Petazzoni 		 * We can't use the DMA mapping API, since we don't
2025ab5afd8SThomas Petazzoni 		 * have a valid 'struct device' pointer
2035ab5afd8SThomas Petazzoni 		 */
2045ab5afd8SThomas Petazzoni 		coherency_wa_buf_phys[i] =
2055ab5afd8SThomas Petazzoni 			virt_to_phys(coherency_wa_buf[i]);
2065ab5afd8SThomas Petazzoni 		BUG_ON(!coherency_wa_buf_phys[i]);
2075ab5afd8SThomas Petazzoni 
2085ab5afd8SThomas Petazzoni 		/*
2095ab5afd8SThomas Petazzoni 		 * Configure the XOR engine for memset operation, with
2105ab5afd8SThomas Petazzoni 		 * a 128 bytes block size
2115ab5afd8SThomas Petazzoni 		 */
2125ab5afd8SThomas Petazzoni 		writel(0x444, xor_base + XOR_CONFIG(i));
2135ab5afd8SThomas Petazzoni 		writel(128, xor_base + XOR_BLOCK_SIZE(i));
2145ab5afd8SThomas Petazzoni 		writel(coherency_wa_buf_phys[i],
2155ab5afd8SThomas Petazzoni 		       xor_base + XOR_DEST_POINTER(i));
2165ab5afd8SThomas Petazzoni 	}
2175ab5afd8SThomas Petazzoni 
2185ab5afd8SThomas Petazzoni 	writel(0x0, xor_base + XOR_INIT_VALUE_LOW);
2195ab5afd8SThomas Petazzoni 	writel(0x0, xor_base + XOR_INIT_VALUE_HIGH);
2205ab5afd8SThomas Petazzoni 
2215ab5afd8SThomas Petazzoni 	coherency_wa_enabled = true;
2225ab5afd8SThomas Petazzoni }
2235ab5afd8SThomas Petazzoni 
224e60304f8SGregory CLEMENT static inline void mvebu_hwcc_sync_io_barrier(void)
225e60304f8SGregory CLEMENT {
2265ab5afd8SThomas Petazzoni 	if (coherency_wa_enabled) {
2275ab5afd8SThomas Petazzoni 		mvebu_hwcc_armada375_sync_io_barrier_wa();
2285ab5afd8SThomas Petazzoni 		return;
2295ab5afd8SThomas Petazzoni 	}
2305ab5afd8SThomas Petazzoni 
231e60304f8SGregory CLEMENT 	writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET);
232e60304f8SGregory CLEMENT 	while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1);
233e60304f8SGregory CLEMENT }
234e60304f8SGregory CLEMENT 
235e60304f8SGregory CLEMENT static dma_addr_t mvebu_hwcc_dma_map_page(struct device *dev, struct page *page,
236e60304f8SGregory CLEMENT 				  unsigned long offset, size_t size,
237e60304f8SGregory CLEMENT 				  enum dma_data_direction dir,
238e60304f8SGregory CLEMENT 				  struct dma_attrs *attrs)
239e60304f8SGregory CLEMENT {
240e60304f8SGregory CLEMENT 	if (dir != DMA_TO_DEVICE)
241e60304f8SGregory CLEMENT 		mvebu_hwcc_sync_io_barrier();
242e60304f8SGregory CLEMENT 	return pfn_to_dma(dev, page_to_pfn(page)) + offset;
243e60304f8SGregory CLEMENT }
244e60304f8SGregory CLEMENT 
245e60304f8SGregory CLEMENT 
246e60304f8SGregory CLEMENT static void mvebu_hwcc_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
247e60304f8SGregory CLEMENT 			      size_t size, enum dma_data_direction dir,
248e60304f8SGregory CLEMENT 			      struct dma_attrs *attrs)
249e60304f8SGregory CLEMENT {
250e60304f8SGregory CLEMENT 	if (dir != DMA_TO_DEVICE)
251e60304f8SGregory CLEMENT 		mvebu_hwcc_sync_io_barrier();
252e60304f8SGregory CLEMENT }
253e60304f8SGregory CLEMENT 
254e60304f8SGregory CLEMENT static void mvebu_hwcc_dma_sync(struct device *dev, dma_addr_t dma_handle,
255e60304f8SGregory CLEMENT 			size_t size, enum dma_data_direction dir)
256e60304f8SGregory CLEMENT {
257e60304f8SGregory CLEMENT 	if (dir != DMA_TO_DEVICE)
258e60304f8SGregory CLEMENT 		mvebu_hwcc_sync_io_barrier();
259e60304f8SGregory CLEMENT }
260e60304f8SGregory CLEMENT 
261e60304f8SGregory CLEMENT static struct dma_map_ops mvebu_hwcc_dma_ops = {
262e60304f8SGregory CLEMENT 	.alloc			= arm_dma_alloc,
263e60304f8SGregory CLEMENT 	.free			= arm_dma_free,
264e60304f8SGregory CLEMENT 	.mmap			= arm_dma_mmap,
265e60304f8SGregory CLEMENT 	.map_page		= mvebu_hwcc_dma_map_page,
266e60304f8SGregory CLEMENT 	.unmap_page		= mvebu_hwcc_dma_unmap_page,
267e60304f8SGregory CLEMENT 	.get_sgtable		= arm_dma_get_sgtable,
268e60304f8SGregory CLEMENT 	.map_sg			= arm_dma_map_sg,
269e60304f8SGregory CLEMENT 	.unmap_sg		= arm_dma_unmap_sg,
270e60304f8SGregory CLEMENT 	.sync_single_for_cpu	= mvebu_hwcc_dma_sync,
271e60304f8SGregory CLEMENT 	.sync_single_for_device	= mvebu_hwcc_dma_sync,
272e60304f8SGregory CLEMENT 	.sync_sg_for_cpu	= arm_dma_sync_sg_for_cpu,
273e60304f8SGregory CLEMENT 	.sync_sg_for_device	= arm_dma_sync_sg_for_device,
274e60304f8SGregory CLEMENT 	.set_dma_mask		= arm_dma_set_mask,
275e60304f8SGregory CLEMENT };
276e60304f8SGregory CLEMENT 
277e60304f8SGregory CLEMENT static int mvebu_hwcc_platform_notifier(struct notifier_block *nb,
278e60304f8SGregory CLEMENT 				       unsigned long event, void *__dev)
279e60304f8SGregory CLEMENT {
280e60304f8SGregory CLEMENT 	struct device *dev = __dev;
281e60304f8SGregory CLEMENT 
282e60304f8SGregory CLEMENT 	if (event != BUS_NOTIFY_ADD_DEVICE)
283e60304f8SGregory CLEMENT 		return NOTIFY_DONE;
284e60304f8SGregory CLEMENT 	set_dma_ops(dev, &mvebu_hwcc_dma_ops);
285e60304f8SGregory CLEMENT 
286e60304f8SGregory CLEMENT 	return NOTIFY_OK;
287e60304f8SGregory CLEMENT }
288e60304f8SGregory CLEMENT 
289e60304f8SGregory CLEMENT static struct notifier_block mvebu_hwcc_platform_nb = {
290e60304f8SGregory CLEMENT 	.notifier_call = mvebu_hwcc_platform_notifier,
291e60304f8SGregory CLEMENT };
292e60304f8SGregory CLEMENT 
293924d38f4SThomas Petazzoni static void __init armada_370_coherency_init(struct device_node *np)
294009f1315SGregory CLEMENT {
295580ff0eeSThomas Petazzoni 	struct resource res;
296924d38f4SThomas Petazzoni 
297580ff0eeSThomas Petazzoni 	of_address_to_resource(np, 0, &res);
298580ff0eeSThomas Petazzoni 	coherency_phys_base = res.start;
299580ff0eeSThomas Petazzoni 	/*
300580ff0eeSThomas Petazzoni 	 * Ensure secondary CPUs will see the updated value,
301580ff0eeSThomas Petazzoni 	 * which they read before they join the coherency
302580ff0eeSThomas Petazzoni 	 * fabric, and therefore before they are coherent with
303580ff0eeSThomas Petazzoni 	 * the boot CPU cache.
304580ff0eeSThomas Petazzoni 	 */
305580ff0eeSThomas Petazzoni 	sync_cache_w(&coherency_phys_base);
306009f1315SGregory CLEMENT 	coherency_base = of_iomap(np, 0);
307e60304f8SGregory CLEMENT 	coherency_cpu_base = of_iomap(np, 1);
308952f4ca7SGregory CLEMENT 	set_cpu_coherent();
309924d38f4SThomas Petazzoni }
310924d38f4SThomas Petazzoni 
311d0de9323SThomas Petazzoni static void __init armada_375_380_coherency_init(struct device_node *np)
31277fa4b9aSThomas Petazzoni {
31377fa4b9aSThomas Petazzoni 	coherency_cpu_base = of_iomap(np, 0);
31477fa4b9aSThomas Petazzoni }
31577fa4b9aSThomas Petazzoni 
316501f928eSThomas Petazzoni static int coherency_type(void)
317924d38f4SThomas Petazzoni {
318924d38f4SThomas Petazzoni 	struct device_node *np;
3195fbba080SThomas Petazzoni 	const struct of_device_id *match;
320924d38f4SThomas Petazzoni 
3215fbba080SThomas Petazzoni 	np = of_find_matching_node_and_match(NULL, of_coherency_table, &match);
322924d38f4SThomas Petazzoni 	if (np) {
3235fbba080SThomas Petazzoni 		int type = (int) match->data;
324924d38f4SThomas Petazzoni 
325501f928eSThomas Petazzoni 		/* Armada 370/XP coherency works in both UP and SMP */
326924d38f4SThomas Petazzoni 		if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP)
327501f928eSThomas Petazzoni 			return type;
328924d38f4SThomas Petazzoni 
32977fa4b9aSThomas Petazzoni 		/* Armada 375 coherency works only on SMP */
33077fa4b9aSThomas Petazzoni 		else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 && is_smp())
33177fa4b9aSThomas Petazzoni 			return type;
33277fa4b9aSThomas Petazzoni 
333d0de9323SThomas Petazzoni 		/* Armada 380 coherency works only on SMP */
334d0de9323SThomas Petazzoni 		else if (type == COHERENCY_FABRIC_TYPE_ARMADA_380 && is_smp())
335d0de9323SThomas Petazzoni 			return type;
336009f1315SGregory CLEMENT 	}
337009f1315SGregory CLEMENT 
338501f928eSThomas Petazzoni 	return COHERENCY_FABRIC_TYPE_NONE;
339501f928eSThomas Petazzoni }
340501f928eSThomas Petazzoni 
341501f928eSThomas Petazzoni int coherency_available(void)
342501f928eSThomas Petazzoni {
343501f928eSThomas Petazzoni 	return coherency_type() != COHERENCY_FABRIC_TYPE_NONE;
344501f928eSThomas Petazzoni }
345501f928eSThomas Petazzoni 
346501f928eSThomas Petazzoni int __init coherency_init(void)
347501f928eSThomas Petazzoni {
348501f928eSThomas Petazzoni 	int type = coherency_type();
349501f928eSThomas Petazzoni 	struct device_node *np;
350501f928eSThomas Petazzoni 
351501f928eSThomas Petazzoni 	np = of_find_matching_node(NULL, of_coherency_table);
352501f928eSThomas Petazzoni 
353501f928eSThomas Petazzoni 	if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP)
354501f928eSThomas Petazzoni 		armada_370_coherency_init(np);
355d0de9323SThomas Petazzoni 	else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 ||
356d0de9323SThomas Petazzoni 		 type == COHERENCY_FABRIC_TYPE_ARMADA_380)
357d0de9323SThomas Petazzoni 		armada_375_380_coherency_init(np);
358501f928eSThomas Petazzoni 
359009f1315SGregory CLEMENT 	return 0;
360009f1315SGregory CLEMENT }
361865e0527SThomas Petazzoni 
362865e0527SThomas Petazzoni static int __init coherency_late_init(void)
363865e0527SThomas Petazzoni {
3645ab5afd8SThomas Petazzoni 	int type = coherency_type();
3655ab5afd8SThomas Petazzoni 
3665ab5afd8SThomas Petazzoni 	if (type == COHERENCY_FABRIC_TYPE_NONE)
3675ab5afd8SThomas Petazzoni 		return 0;
3685ab5afd8SThomas Petazzoni 
36939438567SThomas Petazzoni 	if (type == COHERENCY_FABRIC_TYPE_ARMADA_375) {
37039438567SThomas Petazzoni 		u32 dev, rev;
37139438567SThomas Petazzoni 
37239438567SThomas Petazzoni 		if (mvebu_get_soc_id(&dev, &rev) == 0 &&
37339438567SThomas Petazzoni 		    rev == ARMADA_375_Z1_REV)
3745ab5afd8SThomas Petazzoni 			armada_375_coherency_init_wa();
37539438567SThomas Petazzoni 	}
3765ab5afd8SThomas Petazzoni 
377865e0527SThomas Petazzoni 	bus_register_notifier(&platform_bus_type,
378865e0527SThomas Petazzoni 			      &mvebu_hwcc_platform_nb);
3795ab5afd8SThomas Petazzoni 
380865e0527SThomas Petazzoni 	return 0;
381865e0527SThomas Petazzoni }
382865e0527SThomas Petazzoni 
383865e0527SThomas Petazzoni postcore_initcall(coherency_late_init);
384