linux/arch/sh/mm/cache-sh7705.c
<<
>>
Prefs
   1/*
   2 * arch/sh/mm/cache-sh7705.c
   3 *
   4 * Copyright (C) 1999, 2000  Niibe Yutaka
   5 * Copyright (C) 2004  Alex Song
   6 *
   7 * This file is subject to the terms and conditions of the GNU General Public
   8 * License.  See the file "COPYING" in the main directory of this archive
   9 * for more details.
  10 *
  11 */
  12#include <linux/init.h>
  13#include <linux/mman.h>
  14#include <linux/mm.h>
  15#include <linux/fs.h>
  16#include <linux/threads.h>
  17#include <asm/addrspace.h>
  18#include <asm/page.h>
  19#include <asm/processor.h>
  20#include <asm/cache.h>
  21#include <asm/io.h>
  22#include <linux/uaccess.h>
  23#include <asm/mmu_context.h>
  24#include <asm/cacheflush.h>
  25
  26/*
  27 * The 32KB cache on the SH7705 suffers from the same synonym problem
  28 * as SH4 CPUs
  29 */
  30static inline void cache_wback_all(void)
  31{
  32        unsigned long ways, waysize, addrstart;
  33
  34        ways = current_cpu_data.dcache.ways;
  35        waysize = current_cpu_data.dcache.sets;
  36        waysize <<= current_cpu_data.dcache.entry_shift;
  37
  38        addrstart = CACHE_OC_ADDRESS_ARRAY;
  39
  40        do {
  41                unsigned long addr;
  42
  43                for (addr = addrstart;
  44                     addr < addrstart + waysize;
  45                     addr += current_cpu_data.dcache.linesz) {
  46                        unsigned long data;
  47                        int v = SH_CACHE_UPDATED | SH_CACHE_VALID;
  48
  49                        data = __raw_readl(addr);
  50
  51                        if ((data & v) == v)
  52                                __raw_writel(data & ~v, addr);
  53
  54                }
  55
  56                addrstart += current_cpu_data.dcache.way_incr;
  57        } while (--ways);
  58}
  59
  60/*
  61 * Write back the range of D-cache, and purge the I-cache.
  62 *
  63 * Called from kernel/module.c:sys_init_module and routine for a.out format.
  64 */
  65static void sh7705_flush_icache_range(void *args)
  66{
  67        struct flusher_data *data = args;
  68        unsigned long start, end;
  69
  70        start = data->addr1;
  71        end = data->addr2;
  72
  73        __flush_wback_region((void *)start, end - start);
  74}
  75
  76/*
  77 * Writeback&Invalidate the D-cache of the page
  78 */
  79static void __flush_dcache_page(unsigned long phys)
  80{
  81        unsigned long ways, waysize, addrstart;
  82        unsigned long flags;
  83
  84        phys |= SH_CACHE_VALID;
  85
  86        /*
  87         * Here, phys is the physical address of the page. We check all the
  88         * tags in the cache for those with the same page number as this page
  89         * (by masking off the lowest 2 bits of the 19-bit tag; these bits are
  90         * derived from the offset within in the 4k page). Matching valid
  91         * entries are invalidated.
  92         *
  93         * Since 2 bits of the cache index are derived from the virtual page
  94         * number, knowing this would reduce the number of cache entries to be
  95         * searched by a factor of 4. However this function exists to deal with
  96         * potential cache aliasing, therefore the optimisation is probably not
  97         * possible.
  98         */
  99        local_irq_save(flags);
 100        jump_to_uncached();
 101
 102        ways = current_cpu_data.dcache.ways;
 103        waysize = current_cpu_data.dcache.sets;
 104        waysize <<= current_cpu_data.dcache.entry_shift;
 105
 106        addrstart = CACHE_OC_ADDRESS_ARRAY;
 107
 108        do {
 109                unsigned long addr;
 110
 111                for (addr = addrstart;
 112                     addr < addrstart + waysize;
 113                     addr += current_cpu_data.dcache.linesz) {
 114                        unsigned long data;
 115
 116                        data = __raw_readl(addr) & (0x1ffffC00 | SH_CACHE_VALID);
 117                        if (data == phys) {
 118                                data &= ~(SH_CACHE_VALID | SH_CACHE_UPDATED);
 119                                __raw_writel(data, addr);
 120                        }
 121                }
 122
 123                addrstart += current_cpu_data.dcache.way_incr;
 124        } while (--ways);
 125
 126        back_to_cached();
 127        local_irq_restore(flags);
 128}
 129
 130/*
 131 * Write back & invalidate the D-cache of the page.
 132 * (To avoid "alias" issues)
 133 */
 134static void sh7705_flush_dcache_page(void *arg)
 135{
 136        struct page *page = arg;
 137        struct address_space *mapping = page_mapping_file(page);
 138
 139        if (mapping && !mapping_mapped(mapping))
 140                clear_bit(PG_dcache_clean, &page->flags);
 141        else
 142                __flush_dcache_page(__pa(page_address(page)));
 143}
 144
 145static void sh7705_flush_cache_all(void *args)
 146{
 147        unsigned long flags;
 148
 149        local_irq_save(flags);
 150        jump_to_uncached();
 151
 152        cache_wback_all();
 153        back_to_cached();
 154        local_irq_restore(flags);
 155}
 156
 157/*
 158 * Write back and invalidate I/D-caches for the page.
 159 *
 160 * ADDRESS: Virtual Address (U0 address)
 161 */
 162static void sh7705_flush_cache_page(void *args)
 163{
 164        struct flusher_data *data = args;
 165        unsigned long pfn = data->addr2;
 166
 167        __flush_dcache_page(pfn << PAGE_SHIFT);
 168}
 169
 170/*
 171 * This is called when a page-cache page is about to be mapped into a
 172 * user process' address space.  It offers an opportunity for a
 173 * port to ensure d-cache/i-cache coherency if necessary.
 174 *
 175 * Not entirely sure why this is necessary on SH3 with 32K cache but
 176 * without it we get occasional "Memory fault" when loading a program.
 177 */
 178static void sh7705_flush_icache_page(void *page)
 179{
 180        __flush_purge_region(page_address(page), PAGE_SIZE);
 181}
 182
 183void __init sh7705_cache_init(void)
 184{
 185        local_flush_icache_range        = sh7705_flush_icache_range;
 186        local_flush_dcache_page         = sh7705_flush_dcache_page;
 187        local_flush_cache_all           = sh7705_flush_cache_all;
 188        local_flush_cache_mm            = sh7705_flush_cache_all;
 189        local_flush_cache_dup_mm        = sh7705_flush_cache_all;
 190        local_flush_cache_range         = sh7705_flush_cache_all;
 191        local_flush_cache_page          = sh7705_flush_cache_page;
 192        local_flush_icache_page         = sh7705_flush_icache_page;
 193}
 194