linux/arch/sh/mm/cache-sh2a.c
<<
>>
Prefs
   1/*
   2 * arch/sh/mm/cache-sh2a.c
   3 *
   4 * Copyright (C) 2008 Yoshinori Sato
   5 *
   6 * Released under the terms of the GNU GPL v2.0.
   7 */
   8
   9#include <linux/init.h>
  10#include <linux/mm.h>
  11
  12#include <asm/cache.h>
  13#include <asm/addrspace.h>
  14#include <asm/processor.h>
  15#include <asm/cacheflush.h>
  16#include <asm/io.h>
  17
  18/*
  19 * The maximum number of pages we support up to when doing ranged dcache
  20 * flushing. Anything exceeding this will simply flush the dcache in its
  21 * entirety.
  22 */
  23#define MAX_OCACHE_PAGES        32
  24#define MAX_ICACHE_PAGES        32
  25
  26#ifdef CONFIG_CACHE_WRITEBACK
  27static void sh2a_flush_oc_line(unsigned long v, int way)
  28{
  29        unsigned long addr = (v & 0x000007f0) | (way << 11);
  30        unsigned long data;
  31
  32        data = __raw_readl(CACHE_OC_ADDRESS_ARRAY | addr);
  33        if ((data & CACHE_PHYSADDR_MASK) == (v & CACHE_PHYSADDR_MASK)) {
  34                data &= ~SH_CACHE_UPDATED;
  35                __raw_writel(data, CACHE_OC_ADDRESS_ARRAY | addr);
  36        }
  37}
  38#endif
  39
  40static void sh2a_invalidate_line(unsigned long cache_addr, unsigned long v)
  41{
  42        /* Set associative bit to hit all ways */
  43        unsigned long addr = (v & 0x000007f0) | SH_CACHE_ASSOC;
  44        __raw_writel((addr & CACHE_PHYSADDR_MASK), cache_addr | addr);
  45}
  46
  47/*
  48 * Write back the dirty D-caches, but not invalidate them.
  49 */
  50static void sh2a__flush_wback_region(void *start, int size)
  51{
  52#ifdef CONFIG_CACHE_WRITEBACK
  53        unsigned long v;
  54        unsigned long begin, end;
  55        unsigned long flags;
  56        int nr_ways;
  57
  58        begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
  59        end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
  60                & ~(L1_CACHE_BYTES-1);
  61        nr_ways = current_cpu_data.dcache.ways;
  62
  63        local_irq_save(flags);
  64        jump_to_uncached();
  65
  66        /* If there are too many pages then flush the entire cache */
  67        if (((end - begin) >> PAGE_SHIFT) >= MAX_OCACHE_PAGES) {
  68                begin = CACHE_OC_ADDRESS_ARRAY;
  69                end = begin + (nr_ways * current_cpu_data.dcache.way_size);
  70
  71                for (v = begin; v < end; v += L1_CACHE_BYTES) {
  72                        unsigned long data = __raw_readl(v);
  73                        if (data & SH_CACHE_UPDATED)
  74                                __raw_writel(data & ~SH_CACHE_UPDATED, v);
  75                }
  76        } else {
  77                int way;
  78                for (way = 0; way < nr_ways; way++) {
  79                        for (v = begin; v < end; v += L1_CACHE_BYTES)
  80                                sh2a_flush_oc_line(v, way);
  81                }
  82        }
  83
  84        back_to_cached();
  85        local_irq_restore(flags);
  86#endif
  87}
  88
  89/*
  90 * Write back the dirty D-caches and invalidate them.
  91 */
  92static void sh2a__flush_purge_region(void *start, int size)
  93{
  94        unsigned long v;
  95        unsigned long begin, end;
  96        unsigned long flags;
  97
  98        begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
  99        end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
 100                & ~(L1_CACHE_BYTES-1);
 101
 102        local_irq_save(flags);
 103        jump_to_uncached();
 104
 105        for (v = begin; v < end; v+=L1_CACHE_BYTES) {
 106#ifdef CONFIG_CACHE_WRITEBACK
 107                int way;
 108                int nr_ways = current_cpu_data.dcache.ways;
 109                for (way = 0; way < nr_ways; way++)
 110                        sh2a_flush_oc_line(v, way);
 111#endif
 112                sh2a_invalidate_line(CACHE_OC_ADDRESS_ARRAY, v);
 113        }
 114
 115        back_to_cached();
 116        local_irq_restore(flags);
 117}
 118
 119/*
 120 * Invalidate the D-caches, but no write back please
 121 */
 122static void sh2a__flush_invalidate_region(void *start, int size)
 123{
 124        unsigned long v;
 125        unsigned long begin, end;
 126        unsigned long flags;
 127
 128        begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
 129        end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
 130                & ~(L1_CACHE_BYTES-1);
 131
 132        local_irq_save(flags);
 133        jump_to_uncached();
 134
 135        /* If there are too many pages then just blow the cache */
 136        if (((end - begin) >> PAGE_SHIFT) >= MAX_OCACHE_PAGES) {
 137                __raw_writel(__raw_readl(CCR) | CCR_OCACHE_INVALIDATE, CCR);
 138        } else {
 139                for (v = begin; v < end; v += L1_CACHE_BYTES)
 140                        sh2a_invalidate_line(CACHE_OC_ADDRESS_ARRAY, v);
 141        }
 142
 143        back_to_cached();
 144        local_irq_restore(flags);
 145}
 146
 147/*
 148 * Write back the range of D-cache, and purge the I-cache.
 149 */
 150static void sh2a_flush_icache_range(void *args)
 151{
 152        struct flusher_data *data = args;
 153        unsigned long start, end;
 154        unsigned long v;
 155        unsigned long flags;
 156
 157        start = data->addr1 & ~(L1_CACHE_BYTES-1);
 158        end = (data->addr2 + L1_CACHE_BYTES-1) & ~(L1_CACHE_BYTES-1);
 159
 160#ifdef CONFIG_CACHE_WRITEBACK
 161        sh2a__flush_wback_region((void *)start, end-start);
 162#endif
 163
 164        local_irq_save(flags);
 165        jump_to_uncached();
 166
 167        /* I-Cache invalidate */
 168        /* If there are too many pages then just blow the cache */
 169        if (((end - start) >> PAGE_SHIFT) >= MAX_ICACHE_PAGES) {
 170                __raw_writel(__raw_readl(CCR) | CCR_ICACHE_INVALIDATE, CCR);
 171        } else {
 172                for (v = start; v < end; v += L1_CACHE_BYTES)
 173                        sh2a_invalidate_line(CACHE_IC_ADDRESS_ARRAY, v);
 174        }
 175
 176        back_to_cached();
 177        local_irq_restore(flags);
 178}
 179
 180void __init sh2a_cache_init(void)
 181{
 182        local_flush_icache_range        = sh2a_flush_icache_range;
 183
 184        __flush_wback_region            = sh2a__flush_wback_region;
 185        __flush_purge_region            = sh2a__flush_purge_region;
 186        __flush_invalidate_region       = sh2a__flush_invalidate_region;
 187}
 188