pipe: fix check in "set size" fcntl
[safe/jmp/linux-2.6] / arch / sh / mm / cache-sh4.c
index 92b7d94..2cfae81 100644 (file)
@@ -2,7 +2,7 @@
  * arch/sh/mm/cache-sh4.c
  *
  * Copyright (C) 1999, 2000, 2002  Niibe Yutaka
- * Copyright (C) 2001 - 2007  Paul Mundt
+ * Copyright (C) 2001 - 2009  Paul Mundt
  * Copyright (C) 2003  Richard Curnow
  * Copyright (c) 2007 STMicroelectronics (R&D) Ltd.
  *
@@ -15,6 +15,8 @@
 #include <linux/io.h>
 #include <linux/mutex.h>
 #include <linux/fs.h>
+#include <linux/highmem.h>
+#include <asm/pgtable.h>
 #include <asm/mmu_context.h>
 #include <asm/cacheflush.h>
 
  * flushing. Anything exceeding this will simply flush the dcache in its
  * entirety.
  */
-#define MAX_DCACHE_PAGES       64      /* XXX: Tune for ways */
 #define MAX_ICACHE_PAGES       32
 
-static void __flush_cache_4096(unsigned long addr, unsigned long phys,
+static void __flush_cache_one(unsigned long addr, unsigned long phys,
                               unsigned long exec_offset);
 
 /*
@@ -64,6 +65,7 @@ static void sh4_flush_icache_range(void *args)
 
        for (v = start; v < end; v += L1_CACHE_BYTES) {
                unsigned long icacheaddr;
+               int j, n;
 
                __ocbwb(v);
 
@@ -71,8 +73,10 @@ static void sh4_flush_icache_range(void *args)
                                cpu_data->icache.entry_mask);
 
                /* Clear i-cache line valid-bit */
+               n = boot_cpu_data.icache.n_aliases;
                for (i = 0; i < cpu_data->icache.ways; i++) {
-                       __raw_writel(0, icacheaddr);
+                       for (j = 0; j < n; j++)
+                               __raw_writel(0, icacheaddr + (j * PAGE_SIZE));
                        icacheaddr += cpu_data->icache.way_incr;
                }
        }
@@ -81,22 +85,20 @@ static void sh4_flush_icache_range(void *args)
        local_irq_restore(flags);
 }
 
-static inline void flush_cache_4096(unsigned long start,
-                                   unsigned long phys)
+static inline void flush_cache_one(unsigned long start, unsigned long phys)
 {
        unsigned long flags, exec_offset = 0;
 
        /*
-        * All types of SH-4 require PC to be in P2 to operate on the I-cache.
-        * Some types of SH-4 require PC to be in P2 to operate on the D-cache.
+        * All types of SH-4 require PC to be uncached to operate on the I-cache.
+        * Some types of SH-4 require PC to be uncached to operate on the D-cache.
         */
        if ((boot_cpu_data.flags & CPU_HAS_P2_FLUSH_BUG) ||
            (start < CACHE_OC_ADDRESS_ARRAY))
-               exec_offset = 0x20000000;
+               exec_offset = cached_to_uncached;
 
        local_irq_save(flags);
-       __flush_cache_4096(start | SH_CACHE_ASSOC,
-                          P1SEGADDR(phys), exec_offset);
+       __flush_cache_one(start, phys, exec_offset);
        local_irq_restore(flags);
 }
 
@@ -107,6 +109,7 @@ static inline void flush_cache_4096(unsigned long start,
 static void sh4_flush_dcache_page(void *arg)
 {
        struct page *page = arg;
+       unsigned long addr = (unsigned long)page_address(page);
 #ifndef CONFIG_SMP
        struct address_space *mapping = page_mapping(page);
 
@@ -114,22 +117,14 @@ static void sh4_flush_dcache_page(void *arg)
                set_bit(PG_dcache_dirty, &page->flags);
        else
 #endif
-       {
-               unsigned long phys = page_to_phys(page);
-               unsigned long addr = CACHE_OC_ADDRESS_ARRAY;
-               int i, n;
-
-               /* Loop all the D-cache */
-               n = boot_cpu_data.dcache.way_incr >> 12;
-               for (i = 0; i < n; i++, addr += 4096)
-                       flush_cache_4096(addr, phys);
-       }
+               flush_cache_one(CACHE_OC_ADDRESS_ARRAY |
+                               (addr & shm_align_mask), page_to_phys(page));
 
        wmb();
 }
 
 /* TODO: Selective icache invalidation through IC address array.. */
-static void __uses_jump_to_uncached flush_icache_all(void)
+static void flush_icache_all(void)
 {
        unsigned long flags, ccr;
 
@@ -137,9 +132,9 @@ static void __uses_jump_to_uncached flush_icache_all(void)
        jump_to_uncached();
 
        /* Flush I-cache */
-       ccr = ctrl_inl(CCR);
+       ccr = __raw_readl(CCR);
        ccr |= CCR_CACHE_ICI;
-       ctrl_outl(ccr, CCR);
+       __raw_writel(ccr, CCR);
 
        /*
         * back_to_cached() will take care of the barrier for us, don't add
@@ -209,44 +204,62 @@ static void sh4_flush_cache_page(void *args)
 {
        struct flusher_data *data = args;
        struct vm_area_struct *vma;
+       struct page *page;
        unsigned long address, pfn, phys;
-       unsigned int alias_mask;
+       int map_coherent = 0;
+       pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
+       pte_t *pte;
+       void *vaddr;
 
        vma = data->vma;
-       address = data->addr1;
+       address = data->addr1 & PAGE_MASK;
        pfn = data->addr2;
        phys = pfn << PAGE_SHIFT;
+       page = pfn_to_page(pfn);
 
        if (cpu_context(smp_processor_id(), vma->vm_mm) == NO_CONTEXT)
                return;
 
-       alias_mask = boot_cpu_data.dcache.alias_mask;
-
-       /* We only need to flush D-cache when we have alias */
-       if ((address^phys) & alias_mask) {
-               /* Loop 4K of the D-cache */
-               flush_cache_4096(
-                       CACHE_OC_ADDRESS_ARRAY | (address & alias_mask),
-                       phys);
-               /* Loop another 4K of the D-cache */
-               flush_cache_4096(
-                       CACHE_OC_ADDRESS_ARRAY | (phys & alias_mask),
-                       phys);
-       }
+       pgd = pgd_offset(vma->vm_mm, address);
+       pud = pud_offset(pgd, address);
+       pmd = pmd_offset(pud, address);
+       pte = pte_offset_kernel(pmd, address);
 
-       alias_mask = boot_cpu_data.icache.alias_mask;
-       if (vma->vm_flags & VM_EXEC) {
+       /* If the page isn't present, there is nothing to do here. */
+       if (!(pte_val(*pte) & _PAGE_PRESENT))
+               return;
+
+       if ((vma->vm_mm == current->active_mm))
+               vaddr = NULL;
+       else {
                /*
-                * Evict entries from the portion of the cache from which code
-                * may have been executed at this address (virtual).  There's
-                * no need to evict from the portion corresponding to the
-                * physical address as for the D-cache, because we know the
-                * kernel has never executed the code through its identity
-                * translation.
+                * Use kmap_coherent or kmap_atomic to do flushes for
+                * another ASID than the current one.
                 */
-               flush_cache_4096(
-                       CACHE_IC_ADDRESS_ARRAY | (address & alias_mask),
-                       phys);
+               map_coherent = (current_cpu_data.dcache.n_aliases &&
+                       !test_bit(PG_dcache_dirty, &page->flags) &&
+                       page_mapped(page));
+               if (map_coherent)
+                       vaddr = kmap_coherent(page, address);
+               else
+                       vaddr = kmap_atomic(page, KM_USER0);
+
+               address = (unsigned long)vaddr;
+       }
+
+       flush_cache_one(CACHE_OC_ADDRESS_ARRAY |
+                       (address & shm_align_mask), phys);
+
+       if (vma->vm_flags & VM_EXEC)
+               flush_icache_all();
+
+       if (vaddr) {
+               if (map_coherent)
+                       kunmap_coherent(vaddr);
+               else
+                       kunmap_atomic(vaddr, KM_USER0);
        }
 }
 
@@ -286,7 +299,7 @@ static void sh4_flush_cache_range(void *args)
 }
 
 /**
- * __flush_cache_4096
+ * __flush_cache_one
  *
  * @addr:  address in memory mapped cache array
  * @phys:  P1 address to flush (has to match tags if addr has 'A' bit
@@ -299,7 +312,7 @@ static void sh4_flush_cache_range(void *args)
  * operation (purge/write-back) is selected by the lower 2 bits of
  * 'phys'.
  */
-static void __flush_cache_4096(unsigned long addr, unsigned long phys,
+static void __flush_cache_one(unsigned long addr, unsigned long phys,
                               unsigned long exec_offset)
 {
        int way_count;
@@ -364,9 +377,9 @@ extern void __weak sh4__flush_region_init(void);
 void __init sh4_cache_init(void)
 {
        printk("PVR=%08x CVR=%08x PRR=%08x\n",
-               ctrl_inl(CCN_PVR),
-               ctrl_inl(CCN_CVR),
-               ctrl_inl(CCN_PRR));
+               __raw_readl(CCN_PVR),
+               __raw_readl(CCN_CVR),
+               __raw_readl(CCN_PRR));
 
        local_flush_icache_range        = sh4_flush_icache_range;
        local_flush_dcache_page         = sh4_flush_dcache_page;