linux-old/arch/sh/mm/cache-sh3.c
<<
>>
Prefs
   1/* $Id: cache-sh3.c,v 1.6 2001/09/10 08:59:59 dwmw2 Exp $
   2 *
   3 *  linux/arch/sh/mm/cache-sh3.c
   4 *
   5 * Copyright (C) 1999, 2000  Niibe Yutaka
   6 *
   7 */
   8
   9#include <linux/init.h>
  10#include <linux/mman.h>
  11#include <linux/mm.h>
  12#include <linux/threads.h>
  13#include <asm/addrspace.h>
  14#include <asm/page.h>
  15#include <asm/pgtable.h>
  16#include <asm/processor.h>
  17#include <asm/cache.h>
  18#include <asm/io.h>
  19#include <asm/uaccess.h>
  20#include <asm/pgalloc.h>
  21#include <asm/mmu_context.h>
  22
  23
  24#define CCR             0xffffffec      /* Address of Cache Control Register */
  25
  26#define CCR_CACHE_CE    0x01    /* Cache Enable */
  27#define CCR_CACHE_WT    0x02    /* Write-Through (for P0,U0,P3) (else writeback) */
  28#define CCR_CACHE_CB    0x04    /* Write-Back (for P1) (else writethrough) */
  29#define CCR_CACHE_CF    0x08    /* Cache Flush */
  30#define CCR_CACHE_RA    0x20    /* RAM mode */
  31
  32#define CCR_CACHE_VAL   (CCR_CACHE_CB|CCR_CACHE_CE)     /* 8k-byte cache, P1-wb, enable */
  33#define CCR_CACHE_INIT  (CCR_CACHE_CF|CCR_CACHE_VAL)    /* 8k-byte cache, CF, P1-wb, enable */
  34
  35#define CACHE_OC_ADDRESS_ARRAY 0xf0000000
  36#define CACHE_VALID       1
  37#define CACHE_UPDATED     2
  38#define CACHE_PHYSADDR_MASK 0x1ffffc00
  39
  40/* 7709A/7729 has 16K cache (256-entry), while 7702 has only 2K(direct)
  41   7702 is not supported (yet) */
  42struct _cache_system_info {
  43        int way_shift;
  44        int entry_mask;
  45        int num_entries;
  46};
  47
  48/* Data at BSS is cleared after setting this variable.
  49   So, we Should not placed this variable at BSS section.
  50   Initialize this, it is placed at data section. */
  51static struct _cache_system_info cache_system_info = {0,};
  52
  53#define CACHE_OC_WAY_SHIFT      (cache_system_info.way_shift)
  54#define CACHE_OC_ENTRY_SHIFT    4
  55#define CACHE_OC_ENTRY_MASK     (cache_system_info.entry_mask)
  56#define CACHE_OC_NUM_ENTRIES    (cache_system_info.num_entries)
  57#define CACHE_OC_NUM_WAYS       4
  58#define CACHE_OC_ASSOC_BIT    (1<<3)
  59
  60
  61/*
  62 * Write back all the cache.
  63 *
  64 * For SH-4, we only need to flush (write back) Operand Cache,
  65 * as Instruction Cache doesn't have "updated" data.
  66 *
  67 * Assumes that this is called in interrupt disabled context, and P2.
  68 * Shuld be INLINE function.
  69 */
  70static inline void cache_wback_all(void)
  71{
  72        unsigned long addr, data, i, j;
  73
  74        for (i=0; i<CACHE_OC_NUM_ENTRIES; i++) {
  75                for (j=0; j<CACHE_OC_NUM_WAYS; j++) {
  76                        addr = CACHE_OC_ADDRESS_ARRAY|(j<<CACHE_OC_WAY_SHIFT)|
  77                                (i<<CACHE_OC_ENTRY_SHIFT);
  78                        data = ctrl_inl(addr);
  79                        if ((data & (CACHE_UPDATED|CACHE_VALID))
  80                            == (CACHE_UPDATED|CACHE_VALID))
  81                                ctrl_outl(data & ~CACHE_UPDATED, addr);
  82                }
  83        }
  84}
  85
  86static void __init
  87detect_cpu_and_cache_system(void)
  88{
  89        unsigned long addr0, addr1, data0, data1, data2, data3;
  90
  91        jump_to_P2();
  92        /*
  93         * Check if the entry shadows or not.
  94         * When shadowed, it's 128-entry system.
  95         * Otherwise, it's 256-entry system.
  96         */
  97        addr0 = CACHE_OC_ADDRESS_ARRAY + (3 << 12);
  98        addr1 = CACHE_OC_ADDRESS_ARRAY + (1 << 12);
  99
 100        /* First, write back & invalidate */
 101        data0  = ctrl_inl(addr0);
 102        ctrl_outl(data0&~(CACHE_VALID|CACHE_UPDATED), addr0);
 103        data1  = ctrl_inl(addr1);
 104        ctrl_outl(data1&~(CACHE_VALID|CACHE_UPDATED), addr1);
 105
 106        /* Next, check if there's shadow or not */
 107        data0 = ctrl_inl(addr0);
 108        data0 ^= CACHE_VALID;
 109        ctrl_outl(data0, addr0);
 110        data1 = ctrl_inl(addr1);
 111        data2 = data1 ^ CACHE_VALID;
 112        ctrl_outl(data2, addr1);
 113        data3 = ctrl_inl(addr0);
 114
 115        /* Lastly, invaliate them. */
 116        ctrl_outl(data0&~CACHE_VALID, addr0);
 117        ctrl_outl(data2&~CACHE_VALID, addr1);
 118        back_to_P1();
 119
 120        if (data0 == data1 && data2 == data3) { /* Shadow */
 121                cache_system_info.way_shift = 11;
 122                cache_system_info.entry_mask = 0x7f0;
 123                cache_system_info.num_entries = 128;
 124                cpu_data->type = CPU_SH7708;
 125        } else {                                /* 7709A or 7729  */
 126                cache_system_info.way_shift = 12;
 127                cache_system_info.entry_mask = 0xff0;
 128                cache_system_info.num_entries = 256;
 129                cpu_data->type = CPU_SH7729;
 130        }
 131}
 132
 133void __init cache_init(void)
 134{
 135        unsigned long ccr;
 136
 137        detect_cpu_and_cache_system();
 138
 139        jump_to_P2();
 140        ccr = ctrl_inl(CCR);
 141        if (ccr & CCR_CACHE_CE)
 142                /*
 143                 * XXX: Should check RA here. 
 144                 * If RA was 1, we only need to flush the half of the caches.
 145                 */
 146                cache_wback_all();
 147
 148        ctrl_outl(CCR_CACHE_INIT, CCR);
 149        back_to_P1();
 150}
 151
 152/*
 153 * Write back the dirty D-caches, but not invalidate them.
 154 *
 155 * Is this really worth it, or should we just alias this routine
 156 * to __flush_purge_region too?
 157 *
 158 * START: Virtual Address (U0, P1, or P3)
 159 * SIZE: Size of the region.
 160 */
 161
 162void __flush_wback_region(void *start, int size)
 163{
 164        unsigned long v, j;
 165        unsigned long begin, end;
 166        unsigned long flags;
 167
 168        begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
 169        end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
 170                & ~(L1_CACHE_BYTES-1);
 171
 172        for (v = begin; v < end; v+=L1_CACHE_BYTES) {
 173                for (j=0; j<CACHE_OC_NUM_WAYS; j++) {
 174                        unsigned long data, addr, p;
 175
 176                        p = __pa(v);
 177                        addr = CACHE_OC_ADDRESS_ARRAY|(j<<CACHE_OC_WAY_SHIFT)|
 178                                (v&CACHE_OC_ENTRY_MASK);
 179                        save_and_cli(flags);
 180                        data = ctrl_inl(addr);
 181                        
 182                        if ((data & CACHE_PHYSADDR_MASK) ==
 183                            (p & CACHE_PHYSADDR_MASK)) {
 184                                data &= ~CACHE_UPDATED;
 185                                ctrl_outl(data, addr);
 186                                restore_flags(flags);
 187                                break;
 188                        }
 189                        restore_flags(flags);
 190                }
 191        }
 192}
 193
 194/*
 195 * Write back the dirty D-caches and invalidate them.
 196 *
 197 * START: Virtual Address (U0, P1, or P3)
 198 * SIZE: Size of the region.
 199 */
 200void __flush_purge_region(void *start, int size)
 201{
 202        unsigned long v;
 203        unsigned long begin, end;
 204
 205        begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
 206        end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
 207                & ~(L1_CACHE_BYTES-1);
 208
 209        for (v = begin; v < end; v+=L1_CACHE_BYTES) {
 210                unsigned long data, addr;
 211
 212                data = (v & 0xfffffc00); /* _Virtual_ address, ~U, ~V */
 213                addr = CACHE_OC_ADDRESS_ARRAY | (v&CACHE_OC_ENTRY_MASK) | 
 214                        CACHE_OC_ASSOC_BIT;
 215                ctrl_outl(data, addr);
 216        }
 217}
 218
 219/*
 220 * No write back please
 221 *
 222 * Except I don't think there's any way to avoid the writeback. So we
 223 * just alias it to __flush_purge_region(). dwmw2.
 224 */
 225void __flush_invalidate_region(void *start, int size)
 226        __attribute__((alias("__flush_purge_region")));
 227
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.