0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/init.h>
0013 #include <linux/of.h>
0014 #include <linux/of_address.h>
0015 #include <linux/highmem.h>
0016 #include <linux/io.h>
0017 #include <asm/cacheflush.h>
0018 #include <asm/cp15.h>
0019 #include <asm/hardware/cache-feroceon-l2.h>
0020
0021 #define L2_WRITETHROUGH_KIRKWOOD BIT(4)
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041 static inline unsigned long l2_get_va(unsigned long paddr)
0042 {
0043 #ifdef CONFIG_HIGHMEM
0044
0045
0046
0047
0048
0049
0050
0051 void *vaddr = kmap_atomic_pfn(paddr >> PAGE_SHIFT);
0052 return (unsigned long)vaddr + (paddr & ~PAGE_MASK);
0053 #else
0054 return __phys_to_virt(paddr);
0055 #endif
0056 }
0057
0058 static inline void l2_put_va(unsigned long vaddr)
0059 {
0060 #ifdef CONFIG_HIGHMEM
0061 kunmap_atomic((void *)vaddr);
0062 #endif
0063 }
0064
0065 static inline void l2_clean_pa(unsigned long addr)
0066 {
0067 __asm__("mcr p15, 1, %0, c15, c9, 3" : : "r" (addr));
0068 }
0069
0070 static inline void l2_clean_pa_range(unsigned long start, unsigned long end)
0071 {
0072 unsigned long va_start, va_end, flags;
0073
0074
0075
0076
0077
0078
0079 BUG_ON((start ^ end) >> PAGE_SHIFT);
0080
0081 va_start = l2_get_va(start);
0082 va_end = va_start + (end - start);
0083 raw_local_irq_save(flags);
0084 __asm__("mcr p15, 1, %0, c15, c9, 4\n\t"
0085 "mcr p15, 1, %1, c15, c9, 5"
0086 : : "r" (va_start), "r" (va_end));
0087 raw_local_irq_restore(flags);
0088 l2_put_va(va_start);
0089 }
0090
0091 static inline void l2_clean_inv_pa(unsigned long addr)
0092 {
0093 __asm__("mcr p15, 1, %0, c15, c10, 3" : : "r" (addr));
0094 }
0095
0096 static inline void l2_inv_pa(unsigned long addr)
0097 {
0098 __asm__("mcr p15, 1, %0, c15, c11, 3" : : "r" (addr));
0099 }
0100
0101 static inline void l2_inv_pa_range(unsigned long start, unsigned long end)
0102 {
0103 unsigned long va_start, va_end, flags;
0104
0105
0106
0107
0108
0109
0110 BUG_ON((start ^ end) >> PAGE_SHIFT);
0111
0112 va_start = l2_get_va(start);
0113 va_end = va_start + (end - start);
0114 raw_local_irq_save(flags);
0115 __asm__("mcr p15, 1, %0, c15, c11, 4\n\t"
0116 "mcr p15, 1, %1, c15, c11, 5"
0117 : : "r" (va_start), "r" (va_end));
0118 raw_local_irq_restore(flags);
0119 l2_put_va(va_start);
0120 }
0121
0122 static inline void l2_inv_all(void)
0123 {
0124 __asm__("mcr p15, 1, %0, c15, c11, 0" : : "r" (0));
0125 }
0126
0127
0128
0129
0130
0131
0132
0133
0134 #define CACHE_LINE_SIZE 32
0135 #define MAX_RANGE_SIZE 1024
0136
0137 static int l2_wt_override;
0138
0139 static unsigned long calc_range_end(unsigned long start, unsigned long end)
0140 {
0141 unsigned long range_end;
0142
0143 BUG_ON(start & (CACHE_LINE_SIZE - 1));
0144 BUG_ON(end & (CACHE_LINE_SIZE - 1));
0145
0146
0147
0148
0149 range_end = end;
0150
0151
0152
0153
0154
0155
0156 if (range_end > start + MAX_RANGE_SIZE)
0157 range_end = start + MAX_RANGE_SIZE;
0158
0159
0160
0161
0162 if (range_end > (start | (PAGE_SIZE - 1)) + 1)
0163 range_end = (start | (PAGE_SIZE - 1)) + 1;
0164
0165 return range_end;
0166 }
0167
0168 static void feroceon_l2_inv_range(unsigned long start, unsigned long end)
0169 {
0170
0171
0172
0173 if (start & (CACHE_LINE_SIZE - 1)) {
0174 l2_clean_inv_pa(start & ~(CACHE_LINE_SIZE - 1));
0175 start = (start | (CACHE_LINE_SIZE - 1)) + 1;
0176 }
0177
0178
0179
0180
0181 if (start < end && end & (CACHE_LINE_SIZE - 1)) {
0182 l2_clean_inv_pa(end & ~(CACHE_LINE_SIZE - 1));
0183 end &= ~(CACHE_LINE_SIZE - 1);
0184 }
0185
0186
0187
0188
0189 while (start < end) {
0190 unsigned long range_end = calc_range_end(start, end);
0191 l2_inv_pa_range(start, range_end - CACHE_LINE_SIZE);
0192 start = range_end;
0193 }
0194
0195 dsb();
0196 }
0197
0198 static void feroceon_l2_clean_range(unsigned long start, unsigned long end)
0199 {
0200
0201
0202
0203
0204 if (!l2_wt_override) {
0205 start &= ~(CACHE_LINE_SIZE - 1);
0206 end = (end + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1);
0207 while (start != end) {
0208 unsigned long range_end = calc_range_end(start, end);
0209 l2_clean_pa_range(start, range_end - CACHE_LINE_SIZE);
0210 start = range_end;
0211 }
0212 }
0213
0214 dsb();
0215 }
0216
0217 static void feroceon_l2_flush_range(unsigned long start, unsigned long end)
0218 {
0219 start &= ~(CACHE_LINE_SIZE - 1);
0220 end = (end + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1);
0221 while (start != end) {
0222 unsigned long range_end = calc_range_end(start, end);
0223 if (!l2_wt_override)
0224 l2_clean_pa_range(start, range_end - CACHE_LINE_SIZE);
0225 l2_inv_pa_range(start, range_end - CACHE_LINE_SIZE);
0226 start = range_end;
0227 }
0228
0229 dsb();
0230 }
0231
0232
0233
0234
0235
0236
0237
0238 static int __init flush_and_disable_dcache(void)
0239 {
0240 u32 cr;
0241
0242 cr = get_cr();
0243 if (cr & CR_C) {
0244 unsigned long flags;
0245
0246 raw_local_irq_save(flags);
0247 flush_cache_all();
0248 set_cr(cr & ~CR_C);
0249 raw_local_irq_restore(flags);
0250 return 1;
0251 }
0252 return 0;
0253 }
0254
0255 static void __init enable_dcache(void)
0256 {
0257 u32 cr;
0258
0259 cr = get_cr();
0260 set_cr(cr | CR_C);
0261 }
0262
0263 static void __init __invalidate_icache(void)
0264 {
0265 __asm__("mcr p15, 0, %0, c7, c5, 0" : : "r" (0));
0266 }
0267
0268 static int __init invalidate_and_disable_icache(void)
0269 {
0270 u32 cr;
0271
0272 cr = get_cr();
0273 if (cr & CR_I) {
0274 set_cr(cr & ~CR_I);
0275 __invalidate_icache();
0276 return 1;
0277 }
0278 return 0;
0279 }
0280
0281 static void __init enable_icache(void)
0282 {
0283 u32 cr;
0284
0285 cr = get_cr();
0286 set_cr(cr | CR_I);
0287 }
0288
0289 static inline u32 read_extra_features(void)
0290 {
0291 u32 u;
0292
0293 __asm__("mrc p15, 1, %0, c15, c1, 0" : "=r" (u));
0294
0295 return u;
0296 }
0297
0298 static inline void write_extra_features(u32 u)
0299 {
0300 __asm__("mcr p15, 1, %0, c15, c1, 0" : : "r" (u));
0301 }
0302
0303 static void __init disable_l2_prefetch(void)
0304 {
0305 u32 u;
0306
0307
0308
0309
0310
0311 u = read_extra_features();
0312 if (!(u & 0x01000000)) {
0313 pr_info("Feroceon L2: Disabling L2 prefetch.\n");
0314 write_extra_features(u | 0x01000000);
0315 }
0316 }
0317
0318 static void __init enable_l2(void)
0319 {
0320 u32 u;
0321
0322 u = read_extra_features();
0323 if (!(u & 0x00400000)) {
0324 int i, d;
0325
0326 pr_info("Feroceon L2: Enabling L2\n");
0327
0328 d = flush_and_disable_dcache();
0329 i = invalidate_and_disable_icache();
0330 l2_inv_all();
0331 write_extra_features(u | 0x00400000);
0332 if (i)
0333 enable_icache();
0334 if (d)
0335 enable_dcache();
0336 } else
0337 pr_err(FW_BUG
0338 "Feroceon L2: bootloader left the L2 cache on!\n");
0339 }
0340
0341 void __init feroceon_l2_init(int __l2_wt_override)
0342 {
0343 l2_wt_override = __l2_wt_override;
0344
0345 disable_l2_prefetch();
0346
0347 outer_cache.inv_range = feroceon_l2_inv_range;
0348 outer_cache.clean_range = feroceon_l2_clean_range;
0349 outer_cache.flush_range = feroceon_l2_flush_range;
0350
0351 enable_l2();
0352
0353 pr_info("Feroceon L2: Cache support initialised%s.\n",
0354 l2_wt_override ? ", in WT override mode" : "");
0355 }
0356 #ifdef CONFIG_OF
0357 static const struct of_device_id feroceon_ids[] __initconst = {
0358 { .compatible = "marvell,kirkwood-cache"},
0359 { .compatible = "marvell,feroceon-cache"},
0360 {}
0361 };
0362
0363 int __init feroceon_of_init(void)
0364 {
0365 struct device_node *node;
0366 void __iomem *base;
0367 bool l2_wt_override = false;
0368
0369 #if defined(CONFIG_CACHE_FEROCEON_L2_WRITETHROUGH)
0370 l2_wt_override = true;
0371 #endif
0372
0373 node = of_find_matching_node(NULL, feroceon_ids);
0374 if (node && of_device_is_compatible(node, "marvell,kirkwood-cache")) {
0375 base = of_iomap(node, 0);
0376 if (!base)
0377 return -ENOMEM;
0378
0379 if (l2_wt_override)
0380 writel(readl(base) | L2_WRITETHROUGH_KIRKWOOD, base);
0381 else
0382 writel(readl(base) & ~L2_WRITETHROUGH_KIRKWOOD, base);
0383 }
0384
0385 feroceon_l2_init(l2_wt_override);
0386
0387 return 0;
0388 }
0389 #endif