ARM: cache-l2x0: update workaround for PL310 errata 727915
authorColin Cross <ccross@android.com>
Wed, 14 Sep 2011 22:59:50 +0000 (15:59 -0700)
committerColin Cross <ccross@android.com>
Tue, 20 Sep 2011 06:35:45 +0000 (23:35 -0700)
ARM errata 727915 for PL310 has been updated to include a new
workaround required for PL310 r2p0 for l2x0_flush_all, which also
affects l2x0_clean_all in my testing.  For r2p0, clean or flush
each set/way individually.  For r3p0 or greater, use the debug
register for cleaning and flushing.

Requires exporting the cache_id, sets and ways detected in the
init function for later use.

Change-Id: I215055cbe5dc7e4e8184fb2befc4aff672ef0a12
Signed-off-by: Colin Cross <ccross@android.com>
arch/arm/include/asm/hardware/cache-l2x0.h
arch/arm/mm/cache-l2x0.c

index 16bd48031583da44b7c0f25c68027fd787dc3fe1..b3dd3bf617814ca47b9d2e8fcec286e542912625 100644 (file)
@@ -57,6 +57,7 @@
 #define   L2X0_STNDBY_MODE_EN          (1 << 0)
 
 /* Registers shifts and masks */
+#define L2X0_CACHE_ID_REV_MASK         (0x3f)
 #define L2X0_CACHE_ID_PART_MASK                (0xf << 6)
 #define L2X0_CACHE_ID_PART_L210                (1 << 6)
 #define L2X0_CACHE_ID_PART_L310                (3 << 6)
@@ -72,6 +73,8 @@
 #define L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT     29
 #define L2X0_AUX_CTRL_EARLY_BRESP_SHIFT                30
 
+#define REV_PL310_R2P0                         4
+
 #ifndef __ASSEMBLY__
 extern void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask);
 #endif
index 44c086710d2ba5d21a40741290ef8c40de35928c..6f6c1a6fe23cc9e62a1ddcc9e347d8ba32c28e9f 100644 (file)
@@ -29,6 +29,16 @@ static void __iomem *l2x0_base;
 static DEFINE_SPINLOCK(l2x0_lock);
 static uint32_t l2x0_way_mask; /* Bitmask of active ways */
 static uint32_t l2x0_size;
+static u32 l2x0_cache_id;
+static unsigned int l2x0_sets;
+static unsigned int l2x0_ways;
+
+static inline bool is_pl310_rev(int rev)
+{
+       return (l2x0_cache_id &
+               (L2X0_CACHE_ID_PART_MASK | L2X0_CACHE_ID_REV_MASK)) ==
+                       (L2X0_CACHE_ID_PART_L310 | rev);
+}
 
 static inline void cache_wait_way(void __iomem *reg, unsigned long mask)
 {
@@ -120,6 +130,23 @@ static void l2x0_cache_sync(void)
        spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
+#ifdef CONFIG_PL310_ERRATA_727915
+static void l2x0_for_each_set_way(void __iomem *reg)
+{
+       int set;
+       int way;
+       unsigned long flags;
+
+       for (way = 0; way < l2x0_ways; way++) {
+               spin_lock_irqsave(&l2x0_lock, flags);
+               for (set = 0; set < l2x0_sets; set++)
+                       writel_relaxed((way << 28) | (set << 5), reg);
+               cache_sync();
+               spin_unlock_irqrestore(&l2x0_lock, flags);
+       }
+}
+#endif
+
 static void __l2x0_flush_all(void)
 {
        debug_writel(0x03);
@@ -133,6 +160,13 @@ static void l2x0_flush_all(void)
 {
        unsigned long flags;
 
+#ifdef CONFIG_PL310_ERRATA_727915
+       if (is_pl310_rev(REV_PL310_R2P0)) {
+               l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_INV_LINE_IDX);
+               return;
+       }
+#endif
+
        /* clean all ways */
        spin_lock_irqsave(&l2x0_lock, flags);
        __l2x0_flush_all();
@@ -143,11 +177,20 @@ static void l2x0_clean_all(void)
 {
        unsigned long flags;
 
+#ifdef CONFIG_PL310_ERRATA_727915
+       if (is_pl310_rev(REV_PL310_R2P0)) {
+               l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_LINE_IDX);
+               return;
+       }
+#endif
+
        /* clean all ways */
        spin_lock_irqsave(&l2x0_lock, flags);
+       debug_writel(0x03);
        writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);
        cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask);
        cache_sync();
+       debug_writel(0x00);
        spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
@@ -280,47 +323,46 @@ static void l2x0_disable(void)
 void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
 {
        __u32 aux;
-       __u32 cache_id;
        __u32 way_size = 0;
-       int ways;
        const char *type;
 
        l2x0_base = base;
 
-       cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
+       l2x0_cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
        aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
 
        aux &= aux_mask;
        aux |= aux_val;
 
        /* Determine the number of ways */
-       switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
+       switch (l2x0_cache_id & L2X0_CACHE_ID_PART_MASK) {
        case L2X0_CACHE_ID_PART_L310:
                if (aux & (1 << 16))
-                       ways = 16;
+                       l2x0_ways = 16;
                else
-                       ways = 8;
+                       l2x0_ways = 8;
                type = "L310";
                break;
        case L2X0_CACHE_ID_PART_L210:
-               ways = (aux >> 13) & 0xf;
+               l2x0_ways = (aux >> 13) & 0xf;
                type = "L210";
                break;
        default:
                /* Assume unknown chips have 8 ways */
-               ways = 8;
+               l2x0_ways = 8;
                type = "L2x0 series";
                break;
        }
 
-       l2x0_way_mask = (1 << ways) - 1;
+       l2x0_way_mask = (1 << l2x0_ways) - 1;
 
        /*
         * L2 cache Size =  Way size * Number of ways
         */
        way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17;
-       way_size = 1 << (way_size + 3);
-       l2x0_size = ways * way_size * SZ_1K;
+       way_size = SZ_1K << (way_size + 3);
+       l2x0_size = l2x0_ways * way_size;
+       l2x0_sets = way_size / CACHE_LINE_SIZE;
 
        /*
         * Check if l2x0 controller is already enabled.
@@ -349,5 +391,5 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
 
        printk(KERN_INFO "%s cache controller enabled\n", type);
        printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n",
-                       ways, cache_id, aux, l2x0_size);
+                       l2x0_ways, l2x0_cache_id, aux, l2x0_size);
 }