18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * arch/arm/mm/cache-feroceon-l2.c - Feroceon L2 cache controller support 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2008 Marvell Semiconductor 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 78c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 88c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * References: 118c2ecf20Sopenharmony_ci * - Unified Layer 2 Cache for Feroceon CPU Cores, 128c2ecf20Sopenharmony_ci * Document ID MV-S104858-00, Rev. A, October 23 2007. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_address.h> 188c2ecf20Sopenharmony_ci#include <linux/highmem.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 218c2ecf20Sopenharmony_ci#include <asm/cp15.h> 228c2ecf20Sopenharmony_ci#include <asm/hardware/cache-feroceon-l2.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define L2_WRITETHROUGH_KIRKWOOD BIT(4) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * Low-level cache maintenance operations. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * As well as the regular 'clean/invalidate/flush L2 cache line by 308c2ecf20Sopenharmony_ci * MVA' instructions, the Feroceon L2 cache controller also features 318c2ecf20Sopenharmony_ci * 'clean/invalidate L2 range by MVA' operations. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Cache range operations are initiated by writing the start and 348c2ecf20Sopenharmony_ci * end addresses to successive cp15 registers, and process every 358c2ecf20Sopenharmony_ci * cache line whose first byte address lies in the inclusive range 368c2ecf20Sopenharmony_ci * [start:end]. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * The cache range operations stall the CPU pipeline until completion. 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * The range operations require two successive cp15 writes, in 418c2ecf20Sopenharmony_ci * between which we don't want to be preempted. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic inline unsigned long l2_get_va(unsigned long paddr) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci#ifdef CONFIG_HIGHMEM 478c2ecf20Sopenharmony_ci /* 488c2ecf20Sopenharmony_ci * Because range ops can't be done on physical addresses, 498c2ecf20Sopenharmony_ci * we simply install a virtual mapping for it only for the 508c2ecf20Sopenharmony_ci * TLB lookup to occur, hence no need to flush the untouched 518c2ecf20Sopenharmony_ci * memory mapping afterwards (note: a cache flush may happen 528c2ecf20Sopenharmony_ci * in some circumstances depending on the path taken in kunmap_atomic). 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci void *vaddr = kmap_atomic_pfn(paddr >> PAGE_SHIFT); 558c2ecf20Sopenharmony_ci return (unsigned long)vaddr + (paddr & ~PAGE_MASK); 568c2ecf20Sopenharmony_ci#else 578c2ecf20Sopenharmony_ci return __phys_to_virt(paddr); 588c2ecf20Sopenharmony_ci#endif 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic inline void l2_put_va(unsigned long vaddr) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci#ifdef CONFIG_HIGHMEM 648c2ecf20Sopenharmony_ci kunmap_atomic((void *)vaddr); 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic inline void l2_clean_pa(unsigned long addr) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci __asm__("mcr p15, 1, %0, c15, c9, 3" : : "r" (addr)); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic inline void l2_clean_pa_range(unsigned long start, unsigned long end) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci unsigned long va_start, va_end, flags; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * Make sure 'start' and 'end' reference the same page, as 798c2ecf20Sopenharmony_ci * L2 is PIPT and range operations only do a TLB lookup on 808c2ecf20Sopenharmony_ci * the start address. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci BUG_ON((start ^ end) >> PAGE_SHIFT); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci va_start = l2_get_va(start); 858c2ecf20Sopenharmony_ci va_end = va_start + (end - start); 868c2ecf20Sopenharmony_ci raw_local_irq_save(flags); 878c2ecf20Sopenharmony_ci __asm__("mcr p15, 1, %0, c15, c9, 4\n\t" 888c2ecf20Sopenharmony_ci "mcr p15, 1, %1, c15, c9, 5" 898c2ecf20Sopenharmony_ci : : "r" (va_start), "r" (va_end)); 908c2ecf20Sopenharmony_ci raw_local_irq_restore(flags); 918c2ecf20Sopenharmony_ci l2_put_va(va_start); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline void l2_clean_inv_pa(unsigned long addr) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci __asm__("mcr p15, 1, %0, c15, c10, 3" : : "r" (addr)); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic inline void l2_inv_pa(unsigned long addr) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci __asm__("mcr p15, 1, %0, c15, c11, 3" : : "r" (addr)); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic inline void l2_inv_pa_range(unsigned long start, unsigned long end) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci unsigned long va_start, va_end, flags; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * Make sure 'start' and 'end' reference the same page, as 1108c2ecf20Sopenharmony_ci * L2 is PIPT and range operations only do a TLB lookup on 1118c2ecf20Sopenharmony_ci * the start address. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci BUG_ON((start ^ end) >> PAGE_SHIFT); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci va_start = l2_get_va(start); 1168c2ecf20Sopenharmony_ci va_end = va_start + (end - start); 1178c2ecf20Sopenharmony_ci raw_local_irq_save(flags); 1188c2ecf20Sopenharmony_ci __asm__("mcr p15, 1, %0, c15, c11, 4\n\t" 1198c2ecf20Sopenharmony_ci "mcr p15, 1, %1, c15, c11, 5" 1208c2ecf20Sopenharmony_ci : : "r" (va_start), "r" (va_end)); 1218c2ecf20Sopenharmony_ci raw_local_irq_restore(flags); 1228c2ecf20Sopenharmony_ci l2_put_va(va_start); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic inline void l2_inv_all(void) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci __asm__("mcr p15, 1, %0, c15, c11, 0" : : "r" (0)); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* 1318c2ecf20Sopenharmony_ci * Linux primitives. 1328c2ecf20Sopenharmony_ci * 1338c2ecf20Sopenharmony_ci * Note that the end addresses passed to Linux primitives are 1348c2ecf20Sopenharmony_ci * noninclusive, while the hardware cache range operations use 1358c2ecf20Sopenharmony_ci * inclusive start and end addresses. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci#define CACHE_LINE_SIZE 32 1388c2ecf20Sopenharmony_ci#define MAX_RANGE_SIZE 1024 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int l2_wt_override; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic unsigned long calc_range_end(unsigned long start, unsigned long end) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci unsigned long range_end; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci BUG_ON(start & (CACHE_LINE_SIZE - 1)); 1478c2ecf20Sopenharmony_ci BUG_ON(end & (CACHE_LINE_SIZE - 1)); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* 1508c2ecf20Sopenharmony_ci * Try to process all cache lines between 'start' and 'end'. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci range_end = end; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* 1558c2ecf20Sopenharmony_ci * Limit the number of cache lines processed at once, 1568c2ecf20Sopenharmony_ci * since cache range operations stall the CPU pipeline 1578c2ecf20Sopenharmony_ci * until completion. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci if (range_end > start + MAX_RANGE_SIZE) 1608c2ecf20Sopenharmony_ci range_end = start + MAX_RANGE_SIZE; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * Cache range operations can't straddle a page boundary. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci if (range_end > (start | (PAGE_SIZE - 1)) + 1) 1668c2ecf20Sopenharmony_ci range_end = (start | (PAGE_SIZE - 1)) + 1; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return range_end; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void feroceon_l2_inv_range(unsigned long start, unsigned long end) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * Clean and invalidate partial first cache line. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci if (start & (CACHE_LINE_SIZE - 1)) { 1778c2ecf20Sopenharmony_ci l2_clean_inv_pa(start & ~(CACHE_LINE_SIZE - 1)); 1788c2ecf20Sopenharmony_ci start = (start | (CACHE_LINE_SIZE - 1)) + 1; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * Clean and invalidate partial last cache line. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci if (start < end && end & (CACHE_LINE_SIZE - 1)) { 1858c2ecf20Sopenharmony_ci l2_clean_inv_pa(end & ~(CACHE_LINE_SIZE - 1)); 1868c2ecf20Sopenharmony_ci end &= ~(CACHE_LINE_SIZE - 1); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * Invalidate all full cache lines between 'start' and 'end'. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci while (start < end) { 1938c2ecf20Sopenharmony_ci unsigned long range_end = calc_range_end(start, end); 1948c2ecf20Sopenharmony_ci l2_inv_pa_range(start, range_end - CACHE_LINE_SIZE); 1958c2ecf20Sopenharmony_ci start = range_end; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci dsb(); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic void feroceon_l2_clean_range(unsigned long start, unsigned long end) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * If L2 is forced to WT, the L2 will always be clean and we 2058c2ecf20Sopenharmony_ci * don't need to do anything here. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci if (!l2_wt_override) { 2088c2ecf20Sopenharmony_ci start &= ~(CACHE_LINE_SIZE - 1); 2098c2ecf20Sopenharmony_ci end = (end + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1); 2108c2ecf20Sopenharmony_ci while (start != end) { 2118c2ecf20Sopenharmony_ci unsigned long range_end = calc_range_end(start, end); 2128c2ecf20Sopenharmony_ci l2_clean_pa_range(start, range_end - CACHE_LINE_SIZE); 2138c2ecf20Sopenharmony_ci start = range_end; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci dsb(); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void feroceon_l2_flush_range(unsigned long start, unsigned long end) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci start &= ~(CACHE_LINE_SIZE - 1); 2238c2ecf20Sopenharmony_ci end = (end + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1); 2248c2ecf20Sopenharmony_ci while (start != end) { 2258c2ecf20Sopenharmony_ci unsigned long range_end = calc_range_end(start, end); 2268c2ecf20Sopenharmony_ci if (!l2_wt_override) 2278c2ecf20Sopenharmony_ci l2_clean_pa_range(start, range_end - CACHE_LINE_SIZE); 2288c2ecf20Sopenharmony_ci l2_inv_pa_range(start, range_end - CACHE_LINE_SIZE); 2298c2ecf20Sopenharmony_ci start = range_end; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci dsb(); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* 2378c2ecf20Sopenharmony_ci * Routines to disable and re-enable the D-cache and I-cache at run 2388c2ecf20Sopenharmony_ci * time. These are necessary because the L2 cache can only be enabled 2398c2ecf20Sopenharmony_ci * or disabled while the L1 Dcache and Icache are both disabled. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_cistatic int __init flush_and_disable_dcache(void) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci u32 cr; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci cr = get_cr(); 2468c2ecf20Sopenharmony_ci if (cr & CR_C) { 2478c2ecf20Sopenharmony_ci unsigned long flags; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci raw_local_irq_save(flags); 2508c2ecf20Sopenharmony_ci flush_cache_all(); 2518c2ecf20Sopenharmony_ci set_cr(cr & ~CR_C); 2528c2ecf20Sopenharmony_ci raw_local_irq_restore(flags); 2538c2ecf20Sopenharmony_ci return 1; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void __init enable_dcache(void) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci u32 cr; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci cr = get_cr(); 2638c2ecf20Sopenharmony_ci set_cr(cr | CR_C); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic void __init __invalidate_icache(void) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci __asm__("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int __init invalidate_and_disable_icache(void) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci u32 cr; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci cr = get_cr(); 2768c2ecf20Sopenharmony_ci if (cr & CR_I) { 2778c2ecf20Sopenharmony_ci set_cr(cr & ~CR_I); 2788c2ecf20Sopenharmony_ci __invalidate_icache(); 2798c2ecf20Sopenharmony_ci return 1; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void __init enable_icache(void) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci u32 cr; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci cr = get_cr(); 2898c2ecf20Sopenharmony_ci set_cr(cr | CR_I); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic inline u32 read_extra_features(void) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci u32 u; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci __asm__("mrc p15, 1, %0, c15, c1, 0" : "=r" (u)); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return u; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic inline void write_extra_features(u32 u) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci __asm__("mcr p15, 1, %0, c15, c1, 0" : : "r" (u)); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void __init disable_l2_prefetch(void) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci u32 u; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* 3118c2ecf20Sopenharmony_ci * Read the CPU Extra Features register and verify that the 3128c2ecf20Sopenharmony_ci * Disable L2 Prefetch bit is set. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci u = read_extra_features(); 3158c2ecf20Sopenharmony_ci if (!(u & 0x01000000)) { 3168c2ecf20Sopenharmony_ci pr_info("Feroceon L2: Disabling L2 prefetch.\n"); 3178c2ecf20Sopenharmony_ci write_extra_features(u | 0x01000000); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void __init enable_l2(void) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci u32 u; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci u = read_extra_features(); 3268c2ecf20Sopenharmony_ci if (!(u & 0x00400000)) { 3278c2ecf20Sopenharmony_ci int i, d; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci pr_info("Feroceon L2: Enabling L2\n"); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci d = flush_and_disable_dcache(); 3328c2ecf20Sopenharmony_ci i = invalidate_and_disable_icache(); 3338c2ecf20Sopenharmony_ci l2_inv_all(); 3348c2ecf20Sopenharmony_ci write_extra_features(u | 0x00400000); 3358c2ecf20Sopenharmony_ci if (i) 3368c2ecf20Sopenharmony_ci enable_icache(); 3378c2ecf20Sopenharmony_ci if (d) 3388c2ecf20Sopenharmony_ci enable_dcache(); 3398c2ecf20Sopenharmony_ci } else 3408c2ecf20Sopenharmony_ci pr_err(FW_BUG 3418c2ecf20Sopenharmony_ci "Feroceon L2: bootloader left the L2 cache on!\n"); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_civoid __init feroceon_l2_init(int __l2_wt_override) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci l2_wt_override = __l2_wt_override; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci disable_l2_prefetch(); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci outer_cache.inv_range = feroceon_l2_inv_range; 3518c2ecf20Sopenharmony_ci outer_cache.clean_range = feroceon_l2_clean_range; 3528c2ecf20Sopenharmony_ci outer_cache.flush_range = feroceon_l2_flush_range; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci enable_l2(); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci pr_info("Feroceon L2: Cache support initialised%s.\n", 3578c2ecf20Sopenharmony_ci l2_wt_override ? ", in WT override mode" : ""); 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 3608c2ecf20Sopenharmony_cistatic const struct of_device_id feroceon_ids[] __initconst = { 3618c2ecf20Sopenharmony_ci { .compatible = "marvell,kirkwood-cache"}, 3628c2ecf20Sopenharmony_ci { .compatible = "marvell,feroceon-cache"}, 3638c2ecf20Sopenharmony_ci {} 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ciint __init feroceon_of_init(void) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct device_node *node; 3698c2ecf20Sopenharmony_ci void __iomem *base; 3708c2ecf20Sopenharmony_ci bool l2_wt_override = false; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci#if defined(CONFIG_CACHE_FEROCEON_L2_WRITETHROUGH) 3738c2ecf20Sopenharmony_ci l2_wt_override = true; 3748c2ecf20Sopenharmony_ci#endif 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci node = of_find_matching_node(NULL, feroceon_ids); 3778c2ecf20Sopenharmony_ci if (node && of_device_is_compatible(node, "marvell,kirkwood-cache")) { 3788c2ecf20Sopenharmony_ci base = of_iomap(node, 0); 3798c2ecf20Sopenharmony_ci if (!base) 3808c2ecf20Sopenharmony_ci return -ENOMEM; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (l2_wt_override) 3838c2ecf20Sopenharmony_ci writel(readl(base) | L2_WRITETHROUGH_KIRKWOOD, base); 3848c2ecf20Sopenharmony_ci else 3858c2ecf20Sopenharmony_ci writel(readl(base) & ~L2_WRITETHROUGH_KIRKWOOD, base); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci feroceon_l2_init(l2_wt_override); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci#endif 393