aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorThomas Petazzoni <thomas.petazzoni@free-electrons.com>2014-04-14 09:47:05 -0400
committerJason Cooper <jason@lakedaemon.net>2014-04-24 01:00:37 -0400
commit5ab5afd8ba837560f76f8ee527271d2e819bcaef (patch)
treee2c610986de4ce441f5e3d1b76e4cf7c0a542a49 /arch/arm
parent77fa4b9ab0aafc0a06976814ad62aeff990dfd2c (diff)
ARM: mvebu: implement Armada 375 coherency workaround
The early revisions of Armada 375 SOCs (Z1 stepping) have a bug in the I/O coherency unit that prevents using the normal method for the I/O coherency barrier. The recommended workaround is to use a XOR memset transfer to act as the I/O coherency barrier. This involves "borrowing" a XOR engine, which gets disabled in the Device Tree so the normal XOR driver doesn't use it. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Link: https://lkml.kernel.org/r/1397483228-25625-8-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper <jason@lakedaemon.net>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-mvebu/coherency.c168
1 files changed, 165 insertions, 3 deletions
diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c
index 7ccaf87fd772..75db33ef93ee 100644
--- a/arch/arm/mach-mvebu/coherency.c
+++ b/arch/arm/mach-mvebu/coherency.c
@@ -17,6 +17,8 @@
17 * supplies basic routines for configuring and controlling hardware coherency 17 * supplies basic routines for configuring and controlling hardware coherency
18 */ 18 */
19 19
20#define pr_fmt(fmt) "mvebu-coherency: " fmt
21
20#include <linux/kernel.h> 22#include <linux/kernel.h>
21#include <linux/init.h> 23#include <linux/init.h>
22#include <linux/of_address.h> 24#include <linux/of_address.h>
@@ -24,6 +26,9 @@
24#include <linux/smp.h> 26#include <linux/smp.h>
25#include <linux/dma-mapping.h> 27#include <linux/dma-mapping.h>
26#include <linux/platform_device.h> 28#include <linux/platform_device.h>
29#include <linux/slab.h>
30#include <linux/mbus.h>
31#include <linux/clk.h>
27#include <asm/smp_plat.h> 32#include <asm/smp_plat.h>
28#include <asm/cacheflush.h> 33#include <asm/cacheflush.h>
29#include "armada-370-xp.h" 34#include "armada-370-xp.h"
@@ -66,8 +71,157 @@ int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id)
66 return ll_set_cpu_coherent(coherency_base, hw_cpu_id); 71 return ll_set_cpu_coherent(coherency_base, hw_cpu_id);
67} 72}
68 73
74/*
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
69static inline void mvebu_hwcc_sync_io_barrier(void) 218static inline void mvebu_hwcc_sync_io_barrier(void)
70{ 219{
220 if (coherency_wa_enabled) {
221 mvebu_hwcc_armada375_sync_io_barrier_wa();
222 return;
223 }
224
71 writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); 225 writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET);
72 while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); 226 while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1);
73} 227}
@@ -198,9 +352,17 @@ int __init coherency_init(void)
198 352
199static int __init coherency_late_init(void) 353static int __init coherency_late_init(void)
200{ 354{
201 if (coherency_available()) 355 int type = coherency_type();
202 bus_register_notifier(&platform_bus_type, 356
203 &mvebu_hwcc_platform_nb); 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
204 return 0; 366 return 0;
205} 367}
206 368