blob: 75db33ef93ee0da954cc96797754d64920b7c6a5 [file] [log] [blame]
Gregory CLEMENT009f1312012-08-02 11:16:29 +03001/*
2 * Coherency fabric (Aurora) support for Armada 370 and XP platforms.
3 *
4 * Copyright (C) 2012 Marvell
5 *
6 * Yehuda Yitschak <yehuday@marvell.com>
7 * Gregory Clement <gregory.clement@free-electrons.com>
8 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
9 *
10 * This file is licensed under the terms of the GNU General Public
11 * License version 2. This program is licensed "as is" without any
12 * warranty of any kind, whether express or implied.
13 *
14 * The Armada 370 and Armada XP SOCs have a coherency fabric which is
15 * responsible for ensuring hardware coherency between all CPUs and between
16 * CPUs and I/O masters. This file initializes the coherency fabric and
17 * supplies basic routines for configuring and controlling hardware coherency
18 */
19
Thomas Petazzoni5ab5afd2014-04-14 15:47:05 +020020#define pr_fmt(fmt) "mvebu-coherency: " fmt
21
Gregory CLEMENT009f1312012-08-02 11:16:29 +030022#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/of_address.h>
25#include <linux/io.h>
26#include <linux/smp.h>
Gregory CLEMENTe60304f2012-10-12 19:20:36 +020027#include <linux/dma-mapping.h>
28#include <linux/platform_device.h>
Thomas Petazzoni5ab5afd2014-04-14 15:47:05 +020029#include <linux/slab.h>
30#include <linux/mbus.h>
31#include <linux/clk.h>
Gregory CLEMENT009f1312012-08-02 11:16:29 +030032#include <asm/smp_plat.h>
Thomas Petazzoni580ff0e2013-06-06 12:24:28 +020033#include <asm/cacheflush.h>
Gregory CLEMENT009f1312012-08-02 11:16:29 +030034#include "armada-370-xp.h"
Jisheng Zhangb12634e2013-11-07 17:02:38 +080035#include "coherency.h"
Gregory CLEMENT009f1312012-08-02 11:16:29 +030036
Paul Gortmaker8bd26e32013-06-17 15:43:14 -040037unsigned long coherency_phys_base;
Thomas Petazzoni865e0522013-06-05 09:04:55 +020038static void __iomem *coherency_base;
Gregory CLEMENTe60304f2012-10-12 19:20:36 +020039static void __iomem *coherency_cpu_base;
Gregory CLEMENT009f1312012-08-02 11:16:29 +030040
41/* Coherency fabric registers */
42#define COHERENCY_FABRIC_CFG_OFFSET 0x4
43
Gregory CLEMENTe60304f2012-10-12 19:20:36 +020044#define IO_SYNC_BARRIER_CTL_OFFSET 0x0
45
Thomas Petazzoni924d38f2014-04-14 15:46:59 +020046enum {
Thomas Petazzoni501f9282014-04-14 15:47:00 +020047 COHERENCY_FABRIC_TYPE_NONE,
Thomas Petazzoni924d38f2014-04-14 15:46:59 +020048 COHERENCY_FABRIC_TYPE_ARMADA_370_XP,
Thomas Petazzoni77fa4b92014-04-14 15:47:04 +020049 COHERENCY_FABRIC_TYPE_ARMADA_375,
Thomas Petazzoni924d38f2014-04-14 15:46:59 +020050};
51
Gregory CLEMENT009f1312012-08-02 11:16:29 +030052static struct of_device_id of_coherency_table[] = {
Thomas Petazzoni924d38f2014-04-14 15:46:59 +020053 {.compatible = "marvell,coherency-fabric",
54 .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_370_XP },
Thomas Petazzoni77fa4b92014-04-14 15:47:04 +020055 {.compatible = "marvell,armada-375-coherency-fabric",
56 .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_375 },
Gregory CLEMENT009f1312012-08-02 11:16:29 +030057 { /* end of list */ },
58};
59
Gregory CLEMENT009f1312012-08-02 11:16:29 +030060/* Function defined in coherency_ll.S */
61int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id);
62
63int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id)
64{
65 if (!coherency_base) {
66 pr_warn("Can't make CPU %d cache coherent.\n", hw_cpu_id);
67 pr_warn("Coherency fabric is not initialized\n");
68 return 1;
69 }
70
71 return ll_set_cpu_coherent(coherency_base, hw_cpu_id);
72}
73
Thomas Petazzoni5ab5afd2014-04-14 15:47:05 +020074/*
75 * The below code implements the I/O coherency workaround on Armada
76 * 375. This workaround consists in using the two channels of the
77 * first XOR engine to trigger a XOR transaction that serves as the
78 * I/O coherency barrier.
79 */
80
81static void __iomem *xor_base, *xor_high_base;
82static dma_addr_t coherency_wa_buf_phys[CONFIG_NR_CPUS];
83static void *coherency_wa_buf[CONFIG_NR_CPUS];
84static bool coherency_wa_enabled;
85
86#define XOR_CONFIG(chan) (0x10 + (chan * 4))
87#define XOR_ACTIVATION(chan) (0x20 + (chan * 4))
88#define WINDOW_BAR_ENABLE(chan) (0x240 + ((chan) << 2))
89#define WINDOW_BASE(w) (0x250 + ((w) << 2))
90#define WINDOW_SIZE(w) (0x270 + ((w) << 2))
91#define WINDOW_REMAP_HIGH(w) (0x290 + ((w) << 2))
92#define WINDOW_OVERRIDE_CTRL(chan) (0x2A0 + ((chan) << 2))
93#define XOR_DEST_POINTER(chan) (0x2B0 + (chan * 4))
94#define XOR_BLOCK_SIZE(chan) (0x2C0 + (chan * 4))
95#define XOR_INIT_VALUE_LOW 0x2E0
96#define XOR_INIT_VALUE_HIGH 0x2E4
97
98static inline void mvebu_hwcc_armada375_sync_io_barrier_wa(void)
99{
100 int idx = smp_processor_id();
101
102 /* Write '1' to the first word of the buffer */
103 writel(0x1, coherency_wa_buf[idx]);
104
105 /* Wait until the engine is idle */
106 while ((readl(xor_base + XOR_ACTIVATION(idx)) >> 4) & 0x3)
107 ;
108
109 dmb();
110
111 /* Trigger channel */
112 writel(0x1, xor_base + XOR_ACTIVATION(idx));
113
114 /* Poll the data until it is cleared by the XOR transaction */
115 while (readl(coherency_wa_buf[idx]))
116 ;
117}
118
119static void __init armada_375_coherency_init_wa(void)
120{
121 const struct mbus_dram_target_info *dram;
122 struct device_node *xor_node;
123 struct property *xor_status;
124 struct clk *xor_clk;
125 u32 win_enable = 0;
126 int i;
127
128 pr_warn("enabling coherency workaround for Armada 375 Z1, one XOR engine disabled\n");
129
130 /*
131 * Since the workaround uses one XOR engine, we grab a
132 * reference to its Device Tree node first.
133 */
134 xor_node = of_find_compatible_node(NULL, NULL, "marvell,orion-xor");
135 BUG_ON(!xor_node);
136
137 /*
138 * Then we mark it as disabled so that the real XOR driver
139 * will not use it.
140 */
141 xor_status = kzalloc(sizeof(struct property), GFP_KERNEL);
142 BUG_ON(!xor_status);
143
144 xor_status->value = kstrdup("disabled", GFP_KERNEL);
145 BUG_ON(!xor_status->value);
146
147 xor_status->length = 8;
148 xor_status->name = kstrdup("status", GFP_KERNEL);
149 BUG_ON(!xor_status->name);
150
151 of_update_property(xor_node, xor_status);
152
153 /*
154 * And we remap the registers, get the clock, and do the
155 * initial configuration of the XOR engine.
156 */
157 xor_base = of_iomap(xor_node, 0);
158 xor_high_base = of_iomap(xor_node, 1);
159
160 xor_clk = of_clk_get_by_name(xor_node, NULL);
161 BUG_ON(!xor_clk);
162
163 clk_prepare_enable(xor_clk);
164
165 dram = mv_mbus_dram_info();
166
167 for (i = 0; i < 8; i++) {
168 writel(0, xor_base + WINDOW_BASE(i));
169 writel(0, xor_base + WINDOW_SIZE(i));
170 if (i < 4)
171 writel(0, xor_base + WINDOW_REMAP_HIGH(i));
172 }
173
174 for (i = 0; i < dram->num_cs; i++) {
175 const struct mbus_dram_window *cs = dram->cs + i;
176 writel((cs->base & 0xffff0000) |
177 (cs->mbus_attr << 8) |
178 dram->mbus_dram_target_id, xor_base + WINDOW_BASE(i));
179 writel((cs->size - 1) & 0xffff0000, xor_base + WINDOW_SIZE(i));
180
181 win_enable |= (1 << i);
182 win_enable |= 3 << (16 + (2 * i));
183 }
184
185 writel(win_enable, xor_base + WINDOW_BAR_ENABLE(0));
186 writel(win_enable, xor_base + WINDOW_BAR_ENABLE(1));
187 writel(0, xor_base + WINDOW_OVERRIDE_CTRL(0));
188 writel(0, xor_base + WINDOW_OVERRIDE_CTRL(1));
189
190 for (i = 0; i < CONFIG_NR_CPUS; i++) {
191 coherency_wa_buf[i] = kzalloc(PAGE_SIZE, GFP_KERNEL);
192 BUG_ON(!coherency_wa_buf[i]);
193
194 /*
195 * We can't use the DMA mapping API, since we don't
196 * have a valid 'struct device' pointer
197 */
198 coherency_wa_buf_phys[i] =
199 virt_to_phys(coherency_wa_buf[i]);
200 BUG_ON(!coherency_wa_buf_phys[i]);
201
202 /*
203 * Configure the XOR engine for memset operation, with
204 * a 128 bytes block size
205 */
206 writel(0x444, xor_base + XOR_CONFIG(i));
207 writel(128, xor_base + XOR_BLOCK_SIZE(i));
208 writel(coherency_wa_buf_phys[i],
209 xor_base + XOR_DEST_POINTER(i));
210 }
211
212 writel(0x0, xor_base + XOR_INIT_VALUE_LOW);
213 writel(0x0, xor_base + XOR_INIT_VALUE_HIGH);
214
215 coherency_wa_enabled = true;
216}
217
Gregory CLEMENTe60304f2012-10-12 19:20:36 +0200218static inline void mvebu_hwcc_sync_io_barrier(void)
219{
Thomas Petazzoni5ab5afd2014-04-14 15:47:05 +0200220 if (coherency_wa_enabled) {
221 mvebu_hwcc_armada375_sync_io_barrier_wa();
222 return;
223 }
224
Gregory CLEMENTe60304f2012-10-12 19:20:36 +0200225 writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET);
226 while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1);
227}
228
229static dma_addr_t mvebu_hwcc_dma_map_page(struct device *dev, struct page *page,
230 unsigned long offset, size_t size,
231 enum dma_data_direction dir,
232 struct dma_attrs *attrs)
233{
234 if (dir != DMA_TO_DEVICE)
235 mvebu_hwcc_sync_io_barrier();
236 return pfn_to_dma(dev, page_to_pfn(page)) + offset;
237}
238
239
240static void mvebu_hwcc_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
241 size_t size, enum dma_data_direction dir,
242 struct dma_attrs *attrs)
243{
244 if (dir != DMA_TO_DEVICE)
245 mvebu_hwcc_sync_io_barrier();
246}
247
248static void mvebu_hwcc_dma_sync(struct device *dev, dma_addr_t dma_handle,
249 size_t size, enum dma_data_direction dir)
250{
251 if (dir != DMA_TO_DEVICE)
252 mvebu_hwcc_sync_io_barrier();
253}
254
255static struct dma_map_ops mvebu_hwcc_dma_ops = {
256 .alloc = arm_dma_alloc,
257 .free = arm_dma_free,
258 .mmap = arm_dma_mmap,
259 .map_page = mvebu_hwcc_dma_map_page,
260 .unmap_page = mvebu_hwcc_dma_unmap_page,
261 .get_sgtable = arm_dma_get_sgtable,
262 .map_sg = arm_dma_map_sg,
263 .unmap_sg = arm_dma_unmap_sg,
264 .sync_single_for_cpu = mvebu_hwcc_dma_sync,
265 .sync_single_for_device = mvebu_hwcc_dma_sync,
266 .sync_sg_for_cpu = arm_dma_sync_sg_for_cpu,
267 .sync_sg_for_device = arm_dma_sync_sg_for_device,
268 .set_dma_mask = arm_dma_set_mask,
269};
270
271static int mvebu_hwcc_platform_notifier(struct notifier_block *nb,
272 unsigned long event, void *__dev)
273{
274 struct device *dev = __dev;
275
276 if (event != BUS_NOTIFY_ADD_DEVICE)
277 return NOTIFY_DONE;
278 set_dma_ops(dev, &mvebu_hwcc_dma_ops);
279
280 return NOTIFY_OK;
281}
282
283static struct notifier_block mvebu_hwcc_platform_nb = {
284 .notifier_call = mvebu_hwcc_platform_notifier,
285};
286
Thomas Petazzoni924d38f2014-04-14 15:46:59 +0200287static void __init armada_370_coherency_init(struct device_node *np)
288{
289 struct resource res;
290
291 of_address_to_resource(np, 0, &res);
292 coherency_phys_base = res.start;
293 /*
294 * Ensure secondary CPUs will see the updated value,
295 * which they read before they join the coherency
296 * fabric, and therefore before they are coherent with
297 * the boot CPU cache.
298 */
299 sync_cache_w(&coherency_phys_base);
300 coherency_base = of_iomap(np, 0);
301 coherency_cpu_base = of_iomap(np, 1);
302 set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0);
303}
304
Thomas Petazzoni77fa4b92014-04-14 15:47:04 +0200305static void __init armada_375_coherency_init(struct device_node *np)
306{
307 coherency_cpu_base = of_iomap(np, 0);
308}
309
Thomas Petazzoni501f9282014-04-14 15:47:00 +0200310static int coherency_type(void)
Gregory CLEMENT009f1312012-08-02 11:16:29 +0300311{
312 struct device_node *np;
Thomas Petazzoni5fbba082014-04-14 15:47:02 +0200313 const struct of_device_id *match;
Gregory CLEMENT009f1312012-08-02 11:16:29 +0300314
Thomas Petazzoni5fbba082014-04-14 15:47:02 +0200315 np = of_find_matching_node_and_match(NULL, of_coherency_table, &match);
Gregory CLEMENT009f1312012-08-02 11:16:29 +0300316 if (np) {
Thomas Petazzoni5fbba082014-04-14 15:47:02 +0200317 int type = (int) match->data;
Thomas Petazzoni924d38f2014-04-14 15:46:59 +0200318
Thomas Petazzoni501f9282014-04-14 15:47:00 +0200319 /* Armada 370/XP coherency works in both UP and SMP */
Thomas Petazzoni924d38f2014-04-14 15:46:59 +0200320 if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP)
Thomas Petazzoni501f9282014-04-14 15:47:00 +0200321 return type;
Thomas Petazzoni924d38f2014-04-14 15:46:59 +0200322
Thomas Petazzoni77fa4b92014-04-14 15:47:04 +0200323 /* Armada 375 coherency works only on SMP */
324 else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 && is_smp())
325 return type;
326
Jisheng Zhangabe511a2013-08-27 12:41:14 +0800327 of_node_put(np);
Gregory CLEMENT009f1312012-08-02 11:16:29 +0300328 }
329
Thomas Petazzoni501f9282014-04-14 15:47:00 +0200330 return COHERENCY_FABRIC_TYPE_NONE;
331}
332
333int coherency_available(void)
334{
335 return coherency_type() != COHERENCY_FABRIC_TYPE_NONE;
336}
337
338int __init coherency_init(void)
339{
340 int type = coherency_type();
341 struct device_node *np;
342
343 np = of_find_matching_node(NULL, of_coherency_table);
344
345 if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP)
346 armada_370_coherency_init(np);
Thomas Petazzoni77fa4b92014-04-14 15:47:04 +0200347 else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375)
348 armada_375_coherency_init(np);
Thomas Petazzoni501f9282014-04-14 15:47:00 +0200349
Gregory CLEMENT009f1312012-08-02 11:16:29 +0300350 return 0;
351}
Thomas Petazzoni865e0522013-06-05 09:04:55 +0200352
353static int __init coherency_late_init(void)
354{
Thomas Petazzoni5ab5afd2014-04-14 15:47:05 +0200355 int type = coherency_type();
356
357 if (type == COHERENCY_FABRIC_TYPE_NONE)
358 return 0;
359
360 if (type == COHERENCY_FABRIC_TYPE_ARMADA_375)
361 armada_375_coherency_init_wa();
362
363 bus_register_notifier(&platform_bus_type,
364 &mvebu_hwcc_platform_nb);
365
Thomas Petazzoni865e0522013-06-05 09:04:55 +0200366 return 0;
367}
368
369postcore_initcall(coherency_late_init);