| Lennert Buytenhek | 573a652 | 2009-11-24 19:33:52 +0200 | [diff] [blame] | 1 | /* | 
|  | 2 | * arch/arm/mm/cache-tauros2.c - Tauros2 L2 cache controller support | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2008 Marvell Semiconductor | 
|  | 5 | * | 
|  | 6 | * This file is licensed under the terms of the GNU General Public | 
|  | 7 | * License version 2.  This program is licensed "as is" without any | 
|  | 8 | * warranty of any kind, whether express or implied. | 
|  | 9 | * | 
|  | 10 | * References: | 
|  | 11 | * - PJ1 CPU Core Datasheet, | 
|  | 12 | *   Document ID MV-S104837-01, Rev 0.7, January 24 2008. | 
|  | 13 | * - PJ4 CPU Core Datasheet, | 
|  | 14 | *   Document ID MV-S105190-00, Rev 0.7, March 14 2008. | 
|  | 15 | */ | 
|  | 16 |  | 
|  | 17 | #include <linux/init.h> | 
|  | 18 | #include <asm/cacheflush.h> | 
|  | 19 | #include <asm/hardware/cache-tauros2.h> | 
|  | 20 |  | 
|  | 21 |  | 
|  | 22 | /* | 
|  | 23 | * When Tauros2 is used on a CPU that supports the v7 hierarchical | 
|  | 24 | * cache operations, the cache handling code in proc-v7.S takes care | 
|  | 25 | * of everything, including handling DMA coherency. | 
|  | 26 | * | 
|  | 27 | * So, we only need to register outer cache operations here if we're | 
|  | 28 | * being used on a pre-v7 CPU, and we only need to build support for | 
|  | 29 | * outer cache operations into the kernel image if the kernel has been | 
|  | 30 | * configured to support a pre-v7 CPU. | 
|  | 31 | */ | 
|  | 32 | #if __LINUX_ARM_ARCH__ < 7 | 
|  | 33 | /* | 
|  | 34 | * Low-level cache maintenance operations. | 
|  | 35 | */ | 
|  | 36 | static inline void tauros2_clean_pa(unsigned long addr) | 
|  | 37 | { | 
|  | 38 | __asm__("mcr p15, 1, %0, c7, c11, 3" : : "r" (addr)); | 
|  | 39 | } | 
|  | 40 |  | 
|  | 41 | static inline void tauros2_clean_inv_pa(unsigned long addr) | 
|  | 42 | { | 
|  | 43 | __asm__("mcr p15, 1, %0, c7, c15, 3" : : "r" (addr)); | 
|  | 44 | } | 
|  | 45 |  | 
|  | 46 | static inline void tauros2_inv_pa(unsigned long addr) | 
|  | 47 | { | 
|  | 48 | __asm__("mcr p15, 1, %0, c7, c7, 3" : : "r" (addr)); | 
|  | 49 | } | 
|  | 50 |  | 
|  | 51 |  | 
|  | 52 | /* | 
|  | 53 | * Linux primitives. | 
|  | 54 | * | 
|  | 55 | * Note that the end addresses passed to Linux primitives are | 
|  | 56 | * noninclusive. | 
|  | 57 | */ | 
|  | 58 | #define CACHE_LINE_SIZE		32 | 
|  | 59 |  | 
|  | 60 | static void tauros2_inv_range(unsigned long start, unsigned long end) | 
|  | 61 | { | 
|  | 62 | /* | 
|  | 63 | * Clean and invalidate partial first cache line. | 
|  | 64 | */ | 
|  | 65 | if (start & (CACHE_LINE_SIZE - 1)) { | 
|  | 66 | tauros2_clean_inv_pa(start & ~(CACHE_LINE_SIZE - 1)); | 
|  | 67 | start = (start | (CACHE_LINE_SIZE - 1)) + 1; | 
|  | 68 | } | 
|  | 69 |  | 
|  | 70 | /* | 
|  | 71 | * Clean and invalidate partial last cache line. | 
|  | 72 | */ | 
|  | 73 | if (end & (CACHE_LINE_SIZE - 1)) { | 
|  | 74 | tauros2_clean_inv_pa(end & ~(CACHE_LINE_SIZE - 1)); | 
|  | 75 | end &= ~(CACHE_LINE_SIZE - 1); | 
|  | 76 | } | 
|  | 77 |  | 
|  | 78 | /* | 
|  | 79 | * Invalidate all full cache lines between 'start' and 'end'. | 
|  | 80 | */ | 
|  | 81 | while (start < end) { | 
|  | 82 | tauros2_inv_pa(start); | 
|  | 83 | start += CACHE_LINE_SIZE; | 
|  | 84 | } | 
|  | 85 |  | 
|  | 86 | dsb(); | 
|  | 87 | } | 
|  | 88 |  | 
|  | 89 | static void tauros2_clean_range(unsigned long start, unsigned long end) | 
|  | 90 | { | 
|  | 91 | start &= ~(CACHE_LINE_SIZE - 1); | 
|  | 92 | while (start < end) { | 
|  | 93 | tauros2_clean_pa(start); | 
|  | 94 | start += CACHE_LINE_SIZE; | 
|  | 95 | } | 
|  | 96 |  | 
|  | 97 | dsb(); | 
|  | 98 | } | 
|  | 99 |  | 
|  | 100 | static void tauros2_flush_range(unsigned long start, unsigned long end) | 
|  | 101 | { | 
|  | 102 | start &= ~(CACHE_LINE_SIZE - 1); | 
|  | 103 | while (start < end) { | 
|  | 104 | tauros2_clean_inv_pa(start); | 
|  | 105 | start += CACHE_LINE_SIZE; | 
|  | 106 | } | 
|  | 107 |  | 
|  | 108 | dsb(); | 
|  | 109 | } | 
|  | 110 | #endif | 
|  | 111 |  | 
|  | 112 | static inline u32 __init read_extra_features(void) | 
|  | 113 | { | 
|  | 114 | u32 u; | 
|  | 115 |  | 
|  | 116 | __asm__("mrc p15, 1, %0, c15, c1, 0" : "=r" (u)); | 
|  | 117 |  | 
|  | 118 | return u; | 
|  | 119 | } | 
|  | 120 |  | 
|  | 121 | static inline void __init write_extra_features(u32 u) | 
|  | 122 | { | 
|  | 123 | __asm__("mcr p15, 1, %0, c15, c1, 0" : : "r" (u)); | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 | static void __init disable_l2_prefetch(void) | 
|  | 127 | { | 
|  | 128 | u32 u; | 
|  | 129 |  | 
|  | 130 | /* | 
|  | 131 | * Read the CPU Extra Features register and verify that the | 
|  | 132 | * Disable L2 Prefetch bit is set. | 
|  | 133 | */ | 
|  | 134 | u = read_extra_features(); | 
|  | 135 | if (!(u & 0x01000000)) { | 
|  | 136 | printk(KERN_INFO "Tauros2: Disabling L2 prefetch.\n"); | 
|  | 137 | write_extra_features(u | 0x01000000); | 
|  | 138 | } | 
|  | 139 | } | 
|  | 140 |  | 
|  | 141 | static inline int __init cpuid_scheme(void) | 
|  | 142 | { | 
|  | 143 | extern int processor_id; | 
|  | 144 |  | 
|  | 145 | return !!((processor_id & 0x000f0000) == 0x000f0000); | 
|  | 146 | } | 
|  | 147 |  | 
|  | 148 | static inline u32 __init read_mmfr3(void) | 
|  | 149 | { | 
|  | 150 | u32 mmfr3; | 
|  | 151 |  | 
|  | 152 | __asm__("mrc p15, 0, %0, c0, c1, 7\n" : "=r" (mmfr3)); | 
|  | 153 |  | 
|  | 154 | return mmfr3; | 
|  | 155 | } | 
|  | 156 |  | 
|  | 157 | static inline u32 __init read_actlr(void) | 
|  | 158 | { | 
|  | 159 | u32 actlr; | 
|  | 160 |  | 
|  | 161 | __asm__("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr)); | 
|  | 162 |  | 
|  | 163 | return actlr; | 
|  | 164 | } | 
|  | 165 |  | 
|  | 166 | static inline void __init write_actlr(u32 actlr) | 
|  | 167 | { | 
|  | 168 | __asm__("mcr p15, 0, %0, c1, c0, 1\n" : : "r" (actlr)); | 
|  | 169 | } | 
|  | 170 |  | 
|  | 171 | void __init tauros2_init(void) | 
|  | 172 | { | 
|  | 173 | extern int processor_id; | 
|  | 174 | char *mode; | 
|  | 175 |  | 
|  | 176 | disable_l2_prefetch(); | 
|  | 177 |  | 
|  | 178 | #ifdef CONFIG_CPU_32v5 | 
|  | 179 | if ((processor_id & 0xff0f0000) == 0x56050000) { | 
|  | 180 | u32 feat; | 
|  | 181 |  | 
|  | 182 | /* | 
|  | 183 | * v5 CPUs with Tauros2 have the L2 cache enable bit | 
|  | 184 | * located in the CPU Extra Features register. | 
|  | 185 | */ | 
|  | 186 | feat = read_extra_features(); | 
|  | 187 | if (!(feat & 0x00400000)) { | 
|  | 188 | printk(KERN_INFO "Tauros2: Enabling L2 cache.\n"); | 
|  | 189 | write_extra_features(feat | 0x00400000); | 
|  | 190 | } | 
|  | 191 |  | 
|  | 192 | mode = "ARMv5"; | 
|  | 193 | outer_cache.inv_range = tauros2_inv_range; | 
|  | 194 | outer_cache.clean_range = tauros2_clean_range; | 
|  | 195 | outer_cache.flush_range = tauros2_flush_range; | 
|  | 196 | } | 
|  | 197 | #endif | 
|  | 198 |  | 
|  | 199 | #ifdef CONFIG_CPU_32v6 | 
|  | 200 | /* | 
|  | 201 | * Check whether this CPU lacks support for the v7 hierarchical | 
|  | 202 | * cache ops.  (PJ4 is in its v6 personality mode if the MMFR3 | 
|  | 203 | * register indicates no support for the v7 hierarchical cache | 
|  | 204 | * ops.) | 
|  | 205 | */ | 
|  | 206 | if (cpuid_scheme() && (read_mmfr3() & 0xf) == 0) { | 
|  | 207 | /* | 
|  | 208 | * When Tauros2 is used in an ARMv6 system, the L2 | 
|  | 209 | * enable bit is in the ARMv6 ARM-mandated position | 
|  | 210 | * (bit [26] of the System Control Register). | 
|  | 211 | */ | 
|  | 212 | if (!(get_cr() & 0x04000000)) { | 
|  | 213 | printk(KERN_INFO "Tauros2: Enabling L2 cache.\n"); | 
|  | 214 | adjust_cr(0x04000000, 0x04000000); | 
|  | 215 | } | 
|  | 216 |  | 
|  | 217 | mode = "ARMv6"; | 
|  | 218 | outer_cache.inv_range = tauros2_inv_range; | 
|  | 219 | outer_cache.clean_range = tauros2_clean_range; | 
|  | 220 | outer_cache.flush_range = tauros2_flush_range; | 
|  | 221 | } | 
|  | 222 | #endif | 
|  | 223 |  | 
|  | 224 | #ifdef CONFIG_CPU_32v7 | 
|  | 225 | /* | 
|  | 226 | * Check whether this CPU has support for the v7 hierarchical | 
|  | 227 | * cache ops.  (PJ4 is in its v7 personality mode if the MMFR3 | 
|  | 228 | * register indicates support for the v7 hierarchical cache | 
|  | 229 | * ops.) | 
|  | 230 | * | 
|  | 231 | * (Although strictly speaking there may exist CPUs that | 
|  | 232 | * implement the v7 cache ops but are only ARMv6 CPUs (due to | 
|  | 233 | * not complying with all of the other ARMv7 requirements), | 
|  | 234 | * there are no real-life examples of Tauros2 being used on | 
|  | 235 | * such CPUs as of yet.) | 
|  | 236 | */ | 
|  | 237 | if (cpuid_scheme() && (read_mmfr3() & 0xf) == 1) { | 
|  | 238 | u32 actlr; | 
|  | 239 |  | 
|  | 240 | /* | 
|  | 241 | * When Tauros2 is used in an ARMv7 system, the L2 | 
|  | 242 | * enable bit is located in the Auxiliary System Control | 
|  | 243 | * Register (which is the only register allowed by the | 
|  | 244 | * ARMv7 spec to contain fine-grained cache control bits). | 
|  | 245 | */ | 
|  | 246 | actlr = read_actlr(); | 
|  | 247 | if (!(actlr & 0x00000002)) { | 
|  | 248 | printk(KERN_INFO "Tauros2: Enabling L2 cache.\n"); | 
|  | 249 | write_actlr(actlr | 0x00000002); | 
|  | 250 | } | 
|  | 251 |  | 
|  | 252 | mode = "ARMv7"; | 
|  | 253 | } | 
|  | 254 | #endif | 
|  | 255 |  | 
|  | 256 | if (mode == NULL) { | 
|  | 257 | printk(KERN_CRIT "Tauros2: Unable to detect CPU mode.\n"); | 
|  | 258 | return; | 
|  | 259 | } | 
|  | 260 |  | 
|  | 261 | printk(KERN_INFO "Tauros2: L2 cache support initialised " | 
|  | 262 | "in %s mode.\n", mode); | 
|  | 263 | } |