Branch data Line data Source code
1 : : /*
2 : : * arch/arm/mm/cache-l2x0.c - L210/L220 cache controller support
3 : : *
4 : : * Copyright (C) 2007 ARM Limited
5 : : *
6 : : * This program is free software; you can redistribute it and/or modify
7 : : * it under the terms of the GNU General Public License version 2 as
8 : : * published by the Free Software Foundation.
9 : : *
10 : : * This program is distributed in the hope that it will be useful,
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : : * GNU General Public License for more details.
14 : : *
15 : : * You should have received a copy of the GNU General Public License
16 : : * along with this program; if not, write to the Free Software
17 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 : : */
19 : : #include <linux/err.h>
20 : : #include <linux/init.h>
21 : : #include <linux/spinlock.h>
22 : : #include <linux/io.h>
23 : : #include <linux/of.h>
24 : : #include <linux/of_address.h>
25 : :
26 : : #include <asm/cacheflush.h>
27 : : #include <asm/hardware/cache-l2x0.h>
28 : : #include "cache-tauros3.h"
29 : : #include "cache-aurora-l2.h"
30 : :
31 : : #define CACHE_LINE_SIZE 32
32 : :
33 : : static void __iomem *l2x0_base;
34 : : static DEFINE_RAW_SPINLOCK(l2x0_lock);
35 : : static u32 l2x0_way_mask; /* Bitmask of active ways */
36 : : static u32 l2x0_size;
37 : : static u32 l2x0_cache_id;
38 : : static unsigned int l2x0_sets;
39 : : static unsigned int l2x0_ways;
40 : : static unsigned long sync_reg_offset = L2X0_CACHE_SYNC;
41 : :
42 : : /* Aurora don't have the cache ID register available, so we have to
43 : : * pass it though the device tree */
44 : : static u32 cache_id_part_number_from_dt;
45 : :
46 : : struct l2x0_regs l2x0_saved_regs;
47 : :
48 : : struct l2x0_of_data {
49 : : void (*setup)(const struct device_node *, u32 *, u32 *);
50 : : void (*save)(void);
51 : : struct outer_cache_fns outer_cache;
52 : : };
53 : :
54 : : static bool of_init = false;
55 : :
56 : : static inline bool is_pl310_rev(int rev)
57 : : {
58 : : return (l2x0_cache_id &
59 : : (L2X0_CACHE_ID_PART_MASK | L2X0_CACHE_ID_REV_MASK)) ==
60 : : (L2X0_CACHE_ID_PART_L310 | rev);
61 : : }
62 : :
63 : : static inline void cache_wait_way(void __iomem *reg, unsigned long mask)
64 : : {
65 : : /* wait for cache operation by line or way to complete */
66 [ # # # # : 0 : while (readl_relaxed(reg) & mask)
# # ]
67 : 0 : cpu_relax();
68 : : }
69 : :
70 : : #ifdef CONFIG_CACHE_PL310
71 : : static inline void cache_wait(void __iomem *reg, unsigned long mask)
72 : : {
73 : : /* cache operations by line are atomic on PL310 */
74 : : }
75 : : #else
76 : : #define cache_wait cache_wait_way
77 : : #endif
78 : :
79 : : static inline void cache_sync(void)
80 : : {
81 : 0 : void __iomem *base = l2x0_base;
82 : :
83 : 0 : writel_relaxed(0, base + sync_reg_offset);
84 : : cache_wait(base + L2X0_CACHE_SYNC, 1);
85 : : }
86 : :
87 : : static inline void l2x0_clean_line(unsigned long addr)
88 : : {
89 : 0 : void __iomem *base = l2x0_base;
90 : : cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
91 : 0 : writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA);
92 : : }
93 : :
94 : : static inline void l2x0_inv_line(unsigned long addr)
95 : : {
96 : 0 : void __iomem *base = l2x0_base;
97 : : cache_wait(base + L2X0_INV_LINE_PA, 1);
98 : 0 : writel_relaxed(addr, base + L2X0_INV_LINE_PA);
99 : : }
100 : :
101 : : #if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
102 : : static inline void debug_writel(unsigned long val)
103 : : {
104 : : if (outer_cache.set_debug)
105 : : outer_cache.set_debug(val);
106 : : }
107 : :
108 : : static void pl310_set_debug(unsigned long val)
109 : : {
110 : : writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL);
111 : : }
112 : : #else
113 : : /* Optimised out for non-errata case */
114 : : static inline void debug_writel(unsigned long val)
115 : : {
116 : : }
117 : :
118 : : #define pl310_set_debug NULL
119 : : #endif
120 : :
121 : : #ifdef CONFIG_PL310_ERRATA_588369
122 : : static inline void l2x0_flush_line(unsigned long addr)
123 : : {
124 : : void __iomem *base = l2x0_base;
125 : :
126 : : /* Clean by PA followed by Invalidate by PA */
127 : : cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
128 : : writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA);
129 : : cache_wait(base + L2X0_INV_LINE_PA, 1);
130 : : writel_relaxed(addr, base + L2X0_INV_LINE_PA);
131 : : }
132 : : #else
133 : :
134 : : static inline void l2x0_flush_line(unsigned long addr)
135 : : {
136 : 0 : void __iomem *base = l2x0_base;
137 : : cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
138 : 0 : writel_relaxed(addr, base + L2X0_CLEAN_INV_LINE_PA);
139 : : }
140 : : #endif
141 : :
142 : 0 : static void l2x0_cache_sync(void)
143 : : {
144 : : unsigned long flags;
145 : :
146 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
147 : : cache_sync();
148 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
149 : 0 : }
150 : :
151 : : #ifdef CONFIG_PL310_ERRATA_727915
152 : : static void l2x0_for_each_set_way(void __iomem *reg)
153 : : {
154 : : int set;
155 : : int way;
156 : : unsigned long flags;
157 : :
158 : : for (way = 0; way < l2x0_ways; way++) {
159 : : raw_spin_lock_irqsave(&l2x0_lock, flags);
160 : : for (set = 0; set < l2x0_sets; set++)
161 : : writel_relaxed((way << 28) | (set << 5), reg);
162 : : cache_sync();
163 : : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
164 : : }
165 : : }
166 : : #endif
167 : :
168 : 0 : static void __l2x0_flush_all(void)
169 : : {
170 : : debug_writel(0x03);
171 : 0 : writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_INV_WAY);
172 : 0 : cache_wait_way(l2x0_base + L2X0_CLEAN_INV_WAY, l2x0_way_mask);
173 : : cache_sync();
174 : : debug_writel(0x00);
175 : 0 : }
176 : :
177 : 0 : static void l2x0_flush_all(void)
178 : : {
179 : : unsigned long flags;
180 : :
181 : : #ifdef CONFIG_PL310_ERRATA_727915
182 : : if (is_pl310_rev(REV_PL310_R2P0)) {
183 : : l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_INV_LINE_IDX);
184 : : return;
185 : : }
186 : : #endif
187 : :
188 : : /* clean all ways */
189 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
190 : 0 : __l2x0_flush_all();
191 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
192 : 0 : }
193 : :
194 : 0 : static void l2x0_clean_all(void)
195 : : {
196 : : unsigned long flags;
197 : :
198 : : #ifdef CONFIG_PL310_ERRATA_727915
199 : : if (is_pl310_rev(REV_PL310_R2P0)) {
200 : : l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_LINE_IDX);
201 : : return;
202 : : }
203 : : #endif
204 : :
205 : : /* clean all ways */
206 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
207 : : debug_writel(0x03);
208 : 0 : writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);
209 : 0 : cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask);
210 : : cache_sync();
211 : : debug_writel(0x00);
212 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
213 : 0 : }
214 : :
215 : 0 : static void l2x0_inv_all(void)
216 : : {
217 : : unsigned long flags;
218 : :
219 : : /* invalidate all ways */
220 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
221 : : /* Invalidating when L2 is enabled is a nono */
222 [ # # ]: 0 : BUG_ON(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN);
223 : 0 : writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_INV_WAY);
224 : 0 : cache_wait_way(l2x0_base + L2X0_INV_WAY, l2x0_way_mask);
225 : : cache_sync();
226 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
227 : 0 : }
228 : :
229 : 0 : static void l2x0_inv_range(unsigned long start, unsigned long end)
230 : : {
231 : : void __iomem *base = l2x0_base;
232 : : unsigned long flags;
233 : :
234 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
235 [ # # ]: 0 : if (start & (CACHE_LINE_SIZE - 1)) {
236 : 0 : start &= ~(CACHE_LINE_SIZE - 1);
237 : : debug_writel(0x03);
238 : : l2x0_flush_line(start);
239 : : debug_writel(0x00);
240 : 0 : start += CACHE_LINE_SIZE;
241 : : }
242 : :
243 [ # # ]: 0 : if (end & (CACHE_LINE_SIZE - 1)) {
244 : 0 : end &= ~(CACHE_LINE_SIZE - 1);
245 : : debug_writel(0x03);
246 : : l2x0_flush_line(end);
247 : : debug_writel(0x00);
248 : : }
249 : :
250 [ # # ]: 0 : while (start < end) {
251 : 0 : unsigned long blk_end = start + min(end - start, 4096UL);
252 : :
253 [ # # ]: 0 : while (start < blk_end) {
254 : : l2x0_inv_line(start);
255 : 0 : start += CACHE_LINE_SIZE;
256 : : }
257 : :
258 [ # # ]: 0 : if (blk_end < end) {
259 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
260 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
261 : : }
262 : : }
263 : : cache_wait(base + L2X0_INV_LINE_PA, 1);
264 : : cache_sync();
265 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
266 : 0 : }
267 : :
268 : 0 : static void l2x0_clean_range(unsigned long start, unsigned long end)
269 : : {
270 : : void __iomem *base = l2x0_base;
271 : : unsigned long flags;
272 : :
273 [ # # ]: 0 : if ((end - start) >= l2x0_size) {
274 : 0 : l2x0_clean_all();
275 : 0 : return;
276 : : }
277 : :
278 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
279 : 0 : start &= ~(CACHE_LINE_SIZE - 1);
280 [ # # ]: 0 : while (start < end) {
281 : 0 : unsigned long blk_end = start + min(end - start, 4096UL);
282 : :
283 [ # # ]: 0 : while (start < blk_end) {
284 : : l2x0_clean_line(start);
285 : 0 : start += CACHE_LINE_SIZE;
286 : : }
287 : :
288 [ # # ]: 0 : if (blk_end < end) {
289 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
290 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
291 : : }
292 : : }
293 : : cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
294 : : cache_sync();
295 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
296 : : }
297 : :
298 : 0 : static void l2x0_flush_range(unsigned long start, unsigned long end)
299 : : {
300 : : void __iomem *base = l2x0_base;
301 : : unsigned long flags;
302 : :
303 [ # # ]: 0 : if ((end - start) >= l2x0_size) {
304 : 0 : l2x0_flush_all();
305 : 0 : return;
306 : : }
307 : :
308 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
309 : 0 : start &= ~(CACHE_LINE_SIZE - 1);
310 [ # # ]: 0 : while (start < end) {
311 : 0 : unsigned long blk_end = start + min(end - start, 4096UL);
312 : :
313 : : debug_writel(0x03);
314 [ # # ]: 0 : while (start < blk_end) {
315 : : l2x0_flush_line(start);
316 : 0 : start += CACHE_LINE_SIZE;
317 : : }
318 : : debug_writel(0x00);
319 : :
320 [ # # ]: 0 : if (blk_end < end) {
321 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
322 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
323 : : }
324 : : }
325 : : cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
326 : : cache_sync();
327 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
328 : : }
329 : :
330 : 0 : static void l2x0_disable(void)
331 : : {
332 : : unsigned long flags;
333 : :
334 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
335 : 0 : __l2x0_flush_all();
336 : 0 : writel_relaxed(0, l2x0_base + L2X0_CTRL);
337 : 0 : dsb(st);
338 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
339 : 0 : }
340 : :
341 : 0 : static void l2x0_unlock(u32 cache_id)
342 : : {
343 : : int lockregs;
344 : : int i;
345 : :
346 [ # # # ]: 0 : switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
347 : : case L2X0_CACHE_ID_PART_L310:
348 : : lockregs = 8;
349 : : break;
350 : : case AURORA_CACHE_ID:
351 : : lockregs = 4;
352 : 0 : break;
353 : : default:
354 : : /* L210 and unknown types */
355 : : lockregs = 1;
356 : 0 : break;
357 : : }
358 : :
359 [ # # ]: 0 : for (i = 0; i < lockregs; i++) {
360 : 0 : writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_D_BASE +
361 : : i * L2X0_LOCKDOWN_STRIDE);
362 : 0 : writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_I_BASE +
363 : : i * L2X0_LOCKDOWN_STRIDE);
364 : : }
365 : 0 : }
366 : :
367 : 0 : void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
368 : : {
369 : : u32 aux;
370 : : u32 way_size = 0;
371 : : int way_size_shift = L2X0_WAY_SIZE_SHIFT;
372 : : const char *type;
373 : :
374 : 0 : l2x0_base = base;
375 [ # # ]: 0 : if (cache_id_part_number_from_dt)
376 : 0 : l2x0_cache_id = cache_id_part_number_from_dt;
377 : : else
378 : 0 : l2x0_cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
379 : 0 : aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
380 : :
381 : 0 : aux &= aux_mask;
382 : 0 : aux |= aux_val;
383 : :
384 : : /* Determine the number of ways */
385 [ # # # # ]: 0 : switch (l2x0_cache_id & L2X0_CACHE_ID_PART_MASK) {
386 : : case L2X0_CACHE_ID_PART_L310:
387 [ # # ]: 0 : if (aux & (1 << 16))
388 : 0 : l2x0_ways = 16;
389 : : else
390 : 0 : l2x0_ways = 8;
391 : : type = "L310";
392 : : #ifdef CONFIG_PL310_ERRATA_753970
393 : : /* Unmapped register. */
394 : 0 : sync_reg_offset = L2X0_DUMMY_REG;
395 : : #endif
396 [ # # ]: 0 : if ((l2x0_cache_id & L2X0_CACHE_ID_RTL_MASK) <= L2X0_CACHE_ID_RTL_R3P0)
397 : 0 : outer_cache.set_debug = pl310_set_debug;
398 : : break;
399 : : case L2X0_CACHE_ID_PART_L210:
400 : 0 : l2x0_ways = (aux >> 13) & 0xf;
401 : : type = "L210";
402 : 0 : break;
403 : :
404 : : case AURORA_CACHE_ID:
405 : 0 : sync_reg_offset = AURORA_SYNC_REG;
406 : 0 : l2x0_ways = (aux >> 13) & 0xf;
407 : 0 : l2x0_ways = 2 << ((l2x0_ways + 1) >> 2);
408 : : way_size_shift = AURORA_WAY_SIZE_SHIFT;
409 : : type = "Aurora";
410 : 0 : break;
411 : : default:
412 : : /* Assume unknown chips have 8 ways */
413 : 0 : l2x0_ways = 8;
414 : : type = "L2x0 series";
415 : 0 : break;
416 : : }
417 : :
418 : 0 : l2x0_way_mask = (1 << l2x0_ways) - 1;
419 : :
420 : : /*
421 : : * L2 cache Size = Way size * Number of ways
422 : : */
423 : 0 : way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17;
424 : 0 : way_size = SZ_1K << (way_size + way_size_shift);
425 : :
426 : 0 : l2x0_size = l2x0_ways * way_size;
427 : 0 : l2x0_sets = way_size / CACHE_LINE_SIZE;
428 : :
429 : : /*
430 : : * Check if l2x0 controller is already enabled.
431 : : * If you are booting from non-secure mode
432 : : * accessing the below registers will fault.
433 : : */
434 [ # # ]: 0 : if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
435 : : /* Make sure that I&D is not locked down when starting */
436 : 0 : l2x0_unlock(l2x0_cache_id);
437 : :
438 : : /* l2x0 controller is disabled */
439 : 0 : writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL);
440 : :
441 : 0 : l2x0_inv_all();
442 : :
443 : : /* enable L2X0 */
444 : 0 : writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL);
445 : : }
446 : :
447 : : /* Re-read it in case some bits are reserved. */
448 : 0 : aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
449 : :
450 : : /* Save the value for resuming. */
451 : 0 : l2x0_saved_regs.aux_ctrl = aux;
452 : :
453 [ # # ]: 0 : if (!of_init) {
454 : 0 : outer_cache.inv_range = l2x0_inv_range;
455 : 0 : outer_cache.clean_range = l2x0_clean_range;
456 : 0 : outer_cache.flush_range = l2x0_flush_range;
457 : 0 : outer_cache.sync = l2x0_cache_sync;
458 : 0 : outer_cache.flush_all = l2x0_flush_all;
459 : 0 : outer_cache.inv_all = l2x0_inv_all;
460 : 0 : outer_cache.disable = l2x0_disable;
461 : : }
462 : :
463 : 0 : pr_info("%s cache controller enabled\n", type);
464 : 0 : pr_info("l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d kB\n",
465 : : l2x0_ways, l2x0_cache_id, aux, l2x0_size >> 10);
466 : 0 : }
467 : :
468 : : #ifdef CONFIG_OF
469 : : static int l2_wt_override;
470 : :
471 : : /*
472 : : * Note that the end addresses passed to Linux primitives are
473 : : * noninclusive, while the hardware cache range operations use
474 : : * inclusive start and end addresses.
475 : : */
476 : : static unsigned long calc_range_end(unsigned long start, unsigned long end)
477 : : {
478 : : /*
479 : : * Limit the number of cache lines processed at once,
480 : : * since cache range operations stall the CPU pipeline
481 : : * until completion.
482 : : */
483 [ # # ][ # # ]: 0 : if (end > start + MAX_RANGE_SIZE)
[ # # ]
484 : : end = start + MAX_RANGE_SIZE;
485 : :
486 : : /*
487 : : * Cache range operations can't straddle a page boundary.
488 : : */
489 [ # # ][ # # ]: 0 : if (end > PAGE_ALIGN(start+1))
[ # # ]
490 : : end = PAGE_ALIGN(start+1);
491 : :
492 : : return end;
493 : : }
494 : :
495 : : /*
496 : : * Make sure 'start' and 'end' reference the same page, as L2 is PIPT
497 : : * and range operations only do a TLB lookup on the start address.
498 : : */
499 : 0 : static void aurora_pa_range(unsigned long start, unsigned long end,
500 : : unsigned long offset)
501 : : {
502 : : unsigned long flags;
503 : :
504 : 0 : raw_spin_lock_irqsave(&l2x0_lock, flags);
505 : 0 : writel_relaxed(start, l2x0_base + AURORA_RANGE_BASE_ADDR_REG);
506 : 0 : writel_relaxed(end, l2x0_base + offset);
507 : 0 : raw_spin_unlock_irqrestore(&l2x0_lock, flags);
508 : :
509 : : cache_sync();
510 : 0 : }
511 : :
512 : 0 : static void aurora_inv_range(unsigned long start, unsigned long end)
513 : : {
514 : : /*
515 : : * round start and end adresses up to cache line size
516 : : */
517 : 0 : start &= ~(CACHE_LINE_SIZE - 1);
518 : 0 : end = ALIGN(end, CACHE_LINE_SIZE);
519 : :
520 : : /*
521 : : * Invalidate all full cache lines between 'start' and 'end'.
522 : : */
523 [ # # ]: 0 : while (start < end) {
524 : : unsigned long range_end = calc_range_end(start, end);
525 : 0 : aurora_pa_range(start, range_end - CACHE_LINE_SIZE,
526 : : AURORA_INVAL_RANGE_REG);
527 : : start = range_end;
528 : : }
529 : 0 : }
530 : :
531 : 0 : static void aurora_clean_range(unsigned long start, unsigned long end)
532 : : {
533 : : /*
534 : : * If L2 is forced to WT, the L2 will always be clean and we
535 : : * don't need to do anything here.
536 : : */
537 [ # # ]: 0 : if (!l2_wt_override) {
538 : 0 : start &= ~(CACHE_LINE_SIZE - 1);
539 : 0 : end = ALIGN(end, CACHE_LINE_SIZE);
540 [ # # ]: 0 : while (start != end) {
541 : : unsigned long range_end = calc_range_end(start, end);
542 : 0 : aurora_pa_range(start, range_end - CACHE_LINE_SIZE,
543 : : AURORA_CLEAN_RANGE_REG);
544 : : start = range_end;
545 : : }
546 : : }
547 : 0 : }
548 : :
549 : 0 : static void aurora_flush_range(unsigned long start, unsigned long end)
550 : : {
551 : 0 : start &= ~(CACHE_LINE_SIZE - 1);
552 : 0 : end = ALIGN(end, CACHE_LINE_SIZE);
553 [ # # ]: 0 : while (start != end) {
554 : : unsigned long range_end = calc_range_end(start, end);
555 : : /*
556 : : * If L2 is forced to WT, the L2 will always be clean and we
557 : : * just need to invalidate.
558 : : */
559 [ # # ]: 0 : if (l2_wt_override)
560 : 0 : aurora_pa_range(start, range_end - CACHE_LINE_SIZE,
561 : : AURORA_INVAL_RANGE_REG);
562 : : else
563 : 0 : aurora_pa_range(start, range_end - CACHE_LINE_SIZE,
564 : : AURORA_FLUSH_RANGE_REG);
565 : : start = range_end;
566 : : }
567 : 0 : }
568 : :
569 : : /*
570 : : * For certain Broadcom SoCs, depending on the address range, different offsets
571 : : * need to be added to the address before passing it to L2 for
572 : : * invalidation/clean/flush
573 : : *
574 : : * Section Address Range Offset EMI
575 : : * 1 0x00000000 - 0x3FFFFFFF 0x80000000 VC
576 : : * 2 0x40000000 - 0xBFFFFFFF 0x40000000 SYS
577 : : * 3 0xC0000000 - 0xFFFFFFFF 0x80000000 VC
578 : : *
579 : : * When the start and end addresses have crossed two different sections, we
580 : : * need to break the L2 operation into two, each within its own section.
581 : : * For example, if we need to invalidate addresses starts at 0xBFFF0000 and
582 : : * ends at 0xC0001000, we need do invalidate 1) 0xBFFF0000 - 0xBFFFFFFF and 2)
583 : : * 0xC0000000 - 0xC0001000
584 : : *
585 : : * Note 1:
586 : : * By breaking a single L2 operation into two, we may potentially suffer some
587 : : * performance hit, but keep in mind the cross section case is very rare
588 : : *
589 : : * Note 2:
590 : : * We do not need to handle the case when the start address is in
591 : : * Section 1 and the end address is in Section 3, since it is not a valid use
592 : : * case
593 : : *
594 : : * Note 3:
595 : : * Section 1 in practical terms can no longer be used on rev A2. Because of
596 : : * that the code does not need to handle section 1 at all.
597 : : *
598 : : */
599 : : #define BCM_SYS_EMI_START_ADDR 0x40000000UL
600 : : #define BCM_VC_EMI_SEC3_START_ADDR 0xC0000000UL
601 : :
602 : : #define BCM_SYS_EMI_OFFSET 0x40000000UL
603 : : #define BCM_VC_EMI_OFFSET 0x80000000UL
604 : :
605 : : static inline int bcm_addr_is_sys_emi(unsigned long addr)
606 : : {
607 : 0 : return (addr >= BCM_SYS_EMI_START_ADDR) &&
608 : : (addr < BCM_VC_EMI_SEC3_START_ADDR);
609 : : }
610 : :
611 : : static inline unsigned long bcm_l2_phys_addr(unsigned long addr)
612 : : {
613 [ # # ][ # # ]: 0 : if (bcm_addr_is_sys_emi(addr))
[ # # ][ # # ]
[ # # ][ # # ]
614 : 0 : return addr + BCM_SYS_EMI_OFFSET;
615 : : else
616 : 0 : return addr + BCM_VC_EMI_OFFSET;
617 : : }
618 : :
619 : 0 : static void bcm_inv_range(unsigned long start, unsigned long end)
620 : : {
621 : : unsigned long new_start, new_end;
622 : :
623 [ # # ]: 0 : BUG_ON(start < BCM_SYS_EMI_START_ADDR);
624 : :
625 [ # # ]: 0 : if (unlikely(end <= start))
626 : : return;
627 : :
628 : : new_start = bcm_l2_phys_addr(start);
629 : : new_end = bcm_l2_phys_addr(end);
630 : :
631 : : /* normal case, no cross section between start and end */
632 [ # # ][ # # ]: 0 : if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
633 : 0 : l2x0_inv_range(new_start, new_end);
634 : 0 : return;
635 : : }
636 : :
637 : : /* They cross sections, so it can only be a cross from section
638 : : * 2 to section 3
639 : : */
640 : 0 : l2x0_inv_range(new_start,
641 : : bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
642 : 0 : l2x0_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
643 : : new_end);
644 : : }
645 : :
646 : 0 : static void bcm_clean_range(unsigned long start, unsigned long end)
647 : : {
648 : : unsigned long new_start, new_end;
649 : :
650 [ # # ]: 0 : BUG_ON(start < BCM_SYS_EMI_START_ADDR);
651 : :
652 [ # # ]: 0 : if (unlikely(end <= start))
653 : : return;
654 : :
655 [ # # ]: 0 : if ((end - start) >= l2x0_size) {
656 : 0 : l2x0_clean_all();
657 : 0 : return;
658 : : }
659 : :
660 : : new_start = bcm_l2_phys_addr(start);
661 : : new_end = bcm_l2_phys_addr(end);
662 : :
663 : : /* normal case, no cross section between start and end */
664 [ # # ][ # # ]: 0 : if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
665 : 0 : l2x0_clean_range(new_start, new_end);
666 : 0 : return;
667 : : }
668 : :
669 : : /* They cross sections, so it can only be a cross from section
670 : : * 2 to section 3
671 : : */
672 : 0 : l2x0_clean_range(new_start,
673 : : bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
674 : 0 : l2x0_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
675 : : new_end);
676 : : }
677 : :
678 : 0 : static void bcm_flush_range(unsigned long start, unsigned long end)
679 : : {
680 : : unsigned long new_start, new_end;
681 : :
682 [ # # ]: 0 : BUG_ON(start < BCM_SYS_EMI_START_ADDR);
683 : :
684 [ # # ]: 0 : if (unlikely(end <= start))
685 : : return;
686 : :
687 [ # # ]: 0 : if ((end - start) >= l2x0_size) {
688 : 0 : l2x0_flush_all();
689 : 0 : return;
690 : : }
691 : :
692 : : new_start = bcm_l2_phys_addr(start);
693 : : new_end = bcm_l2_phys_addr(end);
694 : :
695 : : /* normal case, no cross section between start and end */
696 [ # # ][ # # ]: 0 : if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
697 : 0 : l2x0_flush_range(new_start, new_end);
698 : 0 : return;
699 : : }
700 : :
701 : : /* They cross sections, so it can only be a cross from section
702 : : * 2 to section 3
703 : : */
704 : 0 : l2x0_flush_range(new_start,
705 : : bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
706 : 0 : l2x0_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
707 : : new_end);
708 : : }
709 : :
710 : 0 : static void __init l2x0_of_setup(const struct device_node *np,
711 : : u32 *aux_val, u32 *aux_mask)
712 : : {
713 : 0 : u32 data[2] = { 0, 0 };
714 : 0 : u32 tag = 0;
715 : 0 : u32 dirty = 0;
716 : : u32 val = 0, mask = 0;
717 : :
718 : : of_property_read_u32(np, "arm,tag-latency", &tag);
719 [ # # ]: 0 : if (tag) {
720 : : mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK;
721 : 0 : val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT;
722 : : }
723 : :
724 : 0 : of_property_read_u32_array(np, "arm,data-latency",
725 : : data, ARRAY_SIZE(data));
726 [ # # ][ # # ]: 0 : if (data[0] && data[1]) {
727 : 0 : mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK |
728 : : L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK;
729 : 0 : val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) |
730 : 0 : ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT);
731 : : }
732 : :
733 : : of_property_read_u32(np, "arm,dirty-latency", &dirty);
734 [ # # ]: 0 : if (dirty) {
735 : 0 : mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK;
736 : 0 : val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT;
737 : : }
738 : :
739 : 0 : *aux_val &= ~mask;
740 : 0 : *aux_val |= val;
741 : 0 : *aux_mask &= ~mask;
742 : 0 : }
743 : :
744 : 0 : static void __init pl310_of_setup(const struct device_node *np,
745 : : u32 *aux_val, u32 *aux_mask)
746 : : {
747 : 0 : u32 data[3] = { 0, 0, 0 };
748 : 0 : u32 tag[3] = { 0, 0, 0 };
749 : 0 : u32 filter[2] = { 0, 0 };
750 : :
751 : 0 : of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag));
752 [ # # ][ # # ]: 0 : if (tag[0] && tag[1] && tag[2])
[ # # ]
753 : 0 : writel_relaxed(
754 : : ((tag[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) |
755 : : ((tag[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) |
756 : : ((tag[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT),
757 : : l2x0_base + L2X0_TAG_LATENCY_CTRL);
758 : :
759 : 0 : of_property_read_u32_array(np, "arm,data-latency",
760 : : data, ARRAY_SIZE(data));
761 [ # # ][ # # ]: 0 : if (data[0] && data[1] && data[2])
[ # # ]
762 : 0 : writel_relaxed(
763 : : ((data[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) |
764 : : ((data[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) |
765 : : ((data[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT),
766 : : l2x0_base + L2X0_DATA_LATENCY_CTRL);
767 : :
768 : 0 : of_property_read_u32_array(np, "arm,filter-ranges",
769 : : filter, ARRAY_SIZE(filter));
770 [ # # ]: 0 : if (filter[1]) {
771 : 0 : writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M),
772 : : l2x0_base + L2X0_ADDR_FILTER_END);
773 : 0 : writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L2X0_ADDR_FILTER_EN,
774 : : l2x0_base + L2X0_ADDR_FILTER_START);
775 : : }
776 : 0 : }
777 : :
778 : 0 : static void __init pl310_save(void)
779 : : {
780 : 0 : u32 l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
781 : : L2X0_CACHE_ID_RTL_MASK;
782 : :
783 : 0 : l2x0_saved_regs.tag_latency = readl_relaxed(l2x0_base +
784 : : L2X0_TAG_LATENCY_CTRL);
785 : 0 : l2x0_saved_regs.data_latency = readl_relaxed(l2x0_base +
786 : : L2X0_DATA_LATENCY_CTRL);
787 : 0 : l2x0_saved_regs.filter_end = readl_relaxed(l2x0_base +
788 : : L2X0_ADDR_FILTER_END);
789 : 0 : l2x0_saved_regs.filter_start = readl_relaxed(l2x0_base +
790 : : L2X0_ADDR_FILTER_START);
791 : :
792 [ # # ]: 0 : if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) {
793 : : /*
794 : : * From r2p0, there is Prefetch offset/control register
795 : : */
796 : 0 : l2x0_saved_regs.prefetch_ctrl = readl_relaxed(l2x0_base +
797 : : L2X0_PREFETCH_CTRL);
798 : : /*
799 : : * From r3p0, there is Power control register
800 : : */
801 [ # # ]: 0 : if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0)
802 : 0 : l2x0_saved_regs.pwr_ctrl = readl_relaxed(l2x0_base +
803 : : L2X0_POWER_CTRL);
804 : : }
805 : 0 : }
806 : :
807 : 0 : static void aurora_save(void)
808 : : {
809 : 0 : l2x0_saved_regs.ctrl = readl_relaxed(l2x0_base + L2X0_CTRL);
810 : 0 : l2x0_saved_regs.aux_ctrl = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
811 : 0 : }
812 : :
813 : 0 : static void __init tauros3_save(void)
814 : : {
815 : 0 : l2x0_saved_regs.aux2_ctrl =
816 : 0 : readl_relaxed(l2x0_base + TAUROS3_AUX2_CTRL);
817 : 0 : l2x0_saved_regs.prefetch_ctrl =
818 : 0 : readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL);
819 : 0 : }
820 : :
821 : 0 : static void l2x0_resume(void)
822 : : {
823 [ # # ]: 0 : if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
824 : : /* restore aux ctrl and enable l2 */
825 : 0 : l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID));
826 : :
827 : 0 : writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base +
828 : : L2X0_AUX_CTRL);
829 : :
830 : 0 : l2x0_inv_all();
831 : :
832 : 0 : writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL);
833 : : }
834 : 0 : }
835 : :
836 : 0 : static void pl310_resume(void)
837 : : {
838 : : u32 l2x0_revision;
839 : :
840 [ # # ]: 0 : if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
841 : : /* restore pl310 setup */
842 : 0 : writel_relaxed(l2x0_saved_regs.tag_latency,
843 : : l2x0_base + L2X0_TAG_LATENCY_CTRL);
844 : 0 : writel_relaxed(l2x0_saved_regs.data_latency,
845 : : l2x0_base + L2X0_DATA_LATENCY_CTRL);
846 : 0 : writel_relaxed(l2x0_saved_regs.filter_end,
847 : : l2x0_base + L2X0_ADDR_FILTER_END);
848 : 0 : writel_relaxed(l2x0_saved_regs.filter_start,
849 : : l2x0_base + L2X0_ADDR_FILTER_START);
850 : :
851 : 0 : l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
852 : : L2X0_CACHE_ID_RTL_MASK;
853 : :
854 [ # # ]: 0 : if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) {
855 : 0 : writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
856 : : l2x0_base + L2X0_PREFETCH_CTRL);
857 [ # # ]: 0 : if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0)
858 : 0 : writel_relaxed(l2x0_saved_regs.pwr_ctrl,
859 : : l2x0_base + L2X0_POWER_CTRL);
860 : : }
861 : : }
862 : :
863 : 0 : l2x0_resume();
864 : 0 : }
865 : :
866 : 0 : static void aurora_resume(void)
867 : : {
868 [ # # ]: 0 : if (!(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
869 : 0 : writel_relaxed(l2x0_saved_regs.aux_ctrl,
870 : : l2x0_base + L2X0_AUX_CTRL);
871 : 0 : writel_relaxed(l2x0_saved_regs.ctrl, l2x0_base + L2X0_CTRL);
872 : : }
873 : 0 : }
874 : :
875 : 0 : static void tauros3_resume(void)
876 : : {
877 [ # # ]: 0 : if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
878 : 0 : writel_relaxed(l2x0_saved_regs.aux2_ctrl,
879 : : l2x0_base + TAUROS3_AUX2_CTRL);
880 : 0 : writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
881 : : l2x0_base + L2X0_PREFETCH_CTRL);
882 : : }
883 : :
884 : 0 : l2x0_resume();
885 : 0 : }
886 : :
887 : 0 : static void __init aurora_broadcast_l2_commands(void)
888 : : {
889 : : __u32 u;
890 : : /* Enable Broadcasting of cache commands to L2*/
891 : 0 : __asm__ __volatile__("mrc p15, 1, %0, c15, c2, 0" : "=r"(u));
892 : 0 : u |= AURORA_CTRL_FW; /* Set the FW bit */
893 : 0 : __asm__ __volatile__("mcr p15, 1, %0, c15, c2, 0\n" : : "r"(u));
894 : 0 : isb();
895 : 0 : }
896 : :
897 : 0 : static void __init aurora_of_setup(const struct device_node *np,
898 : : u32 *aux_val, u32 *aux_mask)
899 : : {
900 : : u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU;
901 : : u32 mask = AURORA_ACR_REPLACEMENT_MASK;
902 : :
903 : : of_property_read_u32(np, "cache-id-part",
904 : : &cache_id_part_number_from_dt);
905 : :
906 : : /* Determine and save the write policy */
907 : 0 : l2_wt_override = of_property_read_bool(np, "wt-override");
908 : :
909 [ # # ]: 0 : if (l2_wt_override) {
910 : : val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY;
911 : : mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK;
912 : : }
913 : :
914 : 0 : *aux_val &= ~mask;
915 : 0 : *aux_val |= val;
916 : 0 : *aux_mask &= ~mask;
917 : 0 : }
918 : :
919 : : static const struct l2x0_of_data pl310_data = {
920 : : .setup = pl310_of_setup,
921 : : .save = pl310_save,
922 : : .outer_cache = {
923 : : .resume = pl310_resume,
924 : : .inv_range = l2x0_inv_range,
925 : : .clean_range = l2x0_clean_range,
926 : : .flush_range = l2x0_flush_range,
927 : : .sync = l2x0_cache_sync,
928 : : .flush_all = l2x0_flush_all,
929 : : .inv_all = l2x0_inv_all,
930 : : .disable = l2x0_disable,
931 : : },
932 : : };
933 : :
934 : : static const struct l2x0_of_data l2x0_data = {
935 : : .setup = l2x0_of_setup,
936 : : .save = NULL,
937 : : .outer_cache = {
938 : : .resume = l2x0_resume,
939 : : .inv_range = l2x0_inv_range,
940 : : .clean_range = l2x0_clean_range,
941 : : .flush_range = l2x0_flush_range,
942 : : .sync = l2x0_cache_sync,
943 : : .flush_all = l2x0_flush_all,
944 : : .inv_all = l2x0_inv_all,
945 : : .disable = l2x0_disable,
946 : : },
947 : : };
948 : :
949 : : static const struct l2x0_of_data aurora_with_outer_data = {
950 : : .setup = aurora_of_setup,
951 : : .save = aurora_save,
952 : : .outer_cache = {
953 : : .resume = aurora_resume,
954 : : .inv_range = aurora_inv_range,
955 : : .clean_range = aurora_clean_range,
956 : : .flush_range = aurora_flush_range,
957 : : .sync = l2x0_cache_sync,
958 : : .flush_all = l2x0_flush_all,
959 : : .inv_all = l2x0_inv_all,
960 : : .disable = l2x0_disable,
961 : : },
962 : : };
963 : :
964 : : static const struct l2x0_of_data aurora_no_outer_data = {
965 : : .setup = aurora_of_setup,
966 : : .save = aurora_save,
967 : : .outer_cache = {
968 : : .resume = aurora_resume,
969 : : },
970 : : };
971 : :
972 : : static const struct l2x0_of_data tauros3_data = {
973 : : .setup = NULL,
974 : : .save = tauros3_save,
975 : : /* Tauros3 broadcasts L1 cache operations to L2 */
976 : : .outer_cache = {
977 : : .resume = tauros3_resume,
978 : : },
979 : : };
980 : :
981 : : static const struct l2x0_of_data bcm_l2x0_data = {
982 : : .setup = pl310_of_setup,
983 : : .save = pl310_save,
984 : : .outer_cache = {
985 : : .resume = pl310_resume,
986 : : .inv_range = bcm_inv_range,
987 : : .clean_range = bcm_clean_range,
988 : : .flush_range = bcm_flush_range,
989 : : .sync = l2x0_cache_sync,
990 : : .flush_all = l2x0_flush_all,
991 : : .inv_all = l2x0_inv_all,
992 : : .disable = l2x0_disable,
993 : : },
994 : : };
995 : :
996 : : static const struct of_device_id l2x0_ids[] __initconst = {
997 : : { .compatible = "arm,l210-cache", .data = (void *)&l2x0_data },
998 : : { .compatible = "arm,l220-cache", .data = (void *)&l2x0_data },
999 : : { .compatible = "arm,pl310-cache", .data = (void *)&pl310_data },
1000 : : { .compatible = "bcm,bcm11351-a2-pl310-cache", /* deprecated name */
1001 : : .data = (void *)&bcm_l2x0_data},
1002 : : { .compatible = "brcm,bcm11351-a2-pl310-cache",
1003 : : .data = (void *)&bcm_l2x0_data},
1004 : : { .compatible = "marvell,aurora-outer-cache",
1005 : : .data = (void *)&aurora_with_outer_data},
1006 : : { .compatible = "marvell,aurora-system-cache",
1007 : : .data = (void *)&aurora_no_outer_data},
1008 : : { .compatible = "marvell,tauros3-cache",
1009 : : .data = (void *)&tauros3_data },
1010 : : {}
1011 : : };
1012 : :
1013 : 0 : int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
1014 : : {
1015 : : struct device_node *np;
1016 : : const struct l2x0_of_data *data;
1017 : : struct resource res;
1018 : :
1019 : : np = of_find_matching_node(NULL, l2x0_ids);
1020 [ # # ]: 0 : if (!np)
1021 : : return -ENODEV;
1022 : :
1023 [ # # ]: 0 : if (of_address_to_resource(np, 0, &res))
1024 : : return -ENODEV;
1025 : :
1026 : 0 : l2x0_base = ioremap(res.start, resource_size(&res));
1027 [ # # ]: 0 : if (!l2x0_base)
1028 : : return -ENOMEM;
1029 : :
1030 : 0 : l2x0_saved_regs.phy_base = res.start;
1031 : :
1032 : 0 : data = of_match_node(l2x0_ids, np)->data;
1033 : :
1034 : : /* L2 configuration can only be changed if the cache is disabled */
1035 [ # # ]: 0 : if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
1036 [ # # ]: 0 : if (data->setup)
1037 : 0 : data->setup(np, &aux_val, &aux_mask);
1038 : :
1039 : : /* For aurora cache in no outer mode select the
1040 : : * correct mode using the coprocessor*/
1041 [ # # ]: 0 : if (data == &aurora_no_outer_data)
1042 : 0 : aurora_broadcast_l2_commands();
1043 : : }
1044 : :
1045 [ # # ]: 0 : if (data->save)
1046 : 0 : data->save();
1047 : :
1048 : 0 : of_init = true;
1049 : 0 : memcpy(&outer_cache, &data->outer_cache, sizeof(outer_cache));
1050 : 0 : l2x0_init(l2x0_base, aux_val, aux_mask);
1051 : :
1052 : 0 : return 0;
1053 : : }
1054 : : #endif
|