diff options
author | Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | 2014-04-14 09:47:05 -0400 |
---|---|---|
committer | Jason Cooper <jason@lakedaemon.net> | 2014-04-24 01:00:37 -0400 |
commit | 5ab5afd8ba837560f76f8ee527271d2e819bcaef (patch) | |
tree | e2c610986de4ce441f5e3d1b76e4cf7c0a542a49 /arch/arm | |
parent | 77fa4b9ab0aafc0a06976814ad62aeff990dfd2c (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.c | 168 |
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 | |||
81 | static void __iomem *xor_base, *xor_high_base; | ||
82 | static dma_addr_t coherency_wa_buf_phys[CONFIG_NR_CPUS]; | ||
83 | static void *coherency_wa_buf[CONFIG_NR_CPUS]; | ||
84 | static 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 | |||
98 | static 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 | |||
119 | static 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 | |||
69 | static inline void mvebu_hwcc_sync_io_barrier(void) | 218 | static 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 | ||
199 | static int __init coherency_late_init(void) | 353 | static 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 | ||