sh: New extended page flag to wire/unwire TLB entries
authorMatt Fleming <matt@console-pimps.org>
Tue, 17 Nov 2009 21:05:31 +0000 (21:05 +0000)
committerMatt Fleming <matt@console-pimps.org>
Sat, 16 Jan 2010 14:28:57 +0000 (14:28 +0000)
Provide a new extended page flag, _PAGE_WIRED and an SH4 implementation
for wiring TLB entries and use it in the fixmap code path so that we can
wire the fixmap TLB entry.

Signed-off-by: Matt Fleming <matt@console-pimps.org>
arch/sh/include/asm/pgtable_32.h
arch/sh/include/asm/tlb.h
arch/sh/include/cpu-sh4/cpu/mmu_context.h
arch/sh/mm/tlb-pteaex.c
arch/sh/mm/tlb-sh4.c

index 5003ee8..c573d45 100644 (file)
@@ -71,6 +71,8 @@
 #define _PAGE_EXT_KERN_WRITE   0x1000  /* EPR4-bit: Kernel space writable */
 #define _PAGE_EXT_KERN_READ    0x2000  /* EPR5-bit: Kernel space readable */
 
+#define _PAGE_EXT_WIRED                0x4000  /* software: Wire TLB entry */
+
 /* Wrapper for extended mode pgprot twiddling */
 #define _PAGE_EXT(x)           ((unsigned long long)(x) << 32)
 
@@ -164,6 +166,8 @@ static inline unsigned long copy_ptea_attributes(unsigned long x)
        (PTE_MASK | _PAGE_ACCESSED | _PAGE_CACHABLE | \
         _PAGE_DIRTY | _PAGE_SPECIAL)
 
+#define _PAGE_WIRED    (_PAGE_EXT(_PAGE_EXT_WIRED))
+
 #ifndef __ASSEMBLY__
 
 #if defined(CONFIG_X2TLB) /* SH-X2 TLB */
index da8fe7a..3ed2f7a 100644 (file)
@@ -97,6 +97,22 @@ tlb_end_vma(struct mmu_gather *tlb, struct vm_area_struct *vma)
 
 #define tlb_migrate_finish(mm)         do { } while (0)
 
+#ifdef CONFIG_CPU_SH4
+extern void tlb_wire_entry(struct vm_area_struct *, unsigned long, pte_t);
+extern void tlb_unwire_entry(void);
+#else
+static inline void tlb_wire_entry(struct vm_area_struct *vma ,
+                                 unsigned long addr, pte_t pte)
+{
+       BUG();
+}
+
+static inline void tlb_unwire_entry(void)
+{
+       BUG();
+}
+#endif /* CONFIG_CPU_SH4 */
+
 #else /* CONFIG_MMU */
 
 #define tlb_start_vma(tlb, vma)                                do { } while (0)
index 3ce7ef6..03ea75c 100644 (file)
 
 #define MMUCR_TI               (1<<2)
 
+#define MMUCR_URB              0x00FC0000
+#define MMUCR_URB_SHIFT                18
+#define MMUCR_URB_NENTRIES     64
+
 #if defined(CONFIG_32BIT) && defined(CONFIG_CPU_SUBTYPE_ST40)
 #define MMUCR_SE               (1 << 4)
 #else
index 409b7c2..88c8bb0 100644 (file)
@@ -76,3 +76,69 @@ void __uses_jump_to_uncached local_flush_tlb_one(unsigned long asid,
        __raw_writel(asid, MMU_UTLB_ADDRESS_ARRAY2 | MMU_PAGE_ASSOC_BIT);
        back_to_cached();
 }
+
+/*
+ * Load the entry for 'addr' into the TLB and wire the entry.
+ */
+void tlb_wire_entry(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
+{
+       unsigned long status, flags;
+       int urb;
+
+       local_irq_save(flags);
+
+       /* Load the entry into the TLB */
+       __update_tlb(vma, addr, pte);
+
+       /* ... and wire it up. */
+       status = ctrl_inl(MMUCR);
+       urb = (status & MMUCR_URB) >> MMUCR_URB_SHIFT;
+       status &= ~MMUCR_URB;
+
+       /*
+        * Make sure we're not trying to wire the last TLB entry slot.
+        */
+       BUG_ON(!--urb);
+
+       urb = urb % MMUCR_URB_NENTRIES;
+
+       status |= (urb << MMUCR_URB_SHIFT);
+       ctrl_outl(status, MMUCR);
+       ctrl_barrier();
+
+       local_irq_restore(flags);
+}
+
+/*
+ * Unwire the last wired TLB entry.
+ *
+ * It should also be noted that it is not possible to wire and unwire
+ * TLB entries in an arbitrary order. If you wire TLB entry N, followed
+ * by entry N+1, you must unwire entry N+1 first, then entry N. In this
+ * respect, it works like a stack or LIFO queue.
+ */
+void tlb_unwire_entry(void)
+{
+       unsigned long status, flags;
+       int urb;
+
+       local_irq_save(flags);
+
+       status = ctrl_inl(MMUCR);
+       urb = (status & MMUCR_URB) >> MMUCR_URB_SHIFT;
+       status &= ~MMUCR_URB;
+
+       /*
+        * Make sure we're not trying to unwire a TLB entry when none
+        * have been wired.
+        */
+       BUG_ON(urb++ == MMUCR_URB_NENTRIES);
+
+       urb = urb % MMUCR_URB_NENTRIES;
+
+       status |= (urb << MMUCR_URB_SHIFT);
+       ctrl_outl(status, MMUCR);
+       ctrl_barrier();
+
+       local_irq_restore(flags);
+}
index 8cf550e..4c62347 100644 (file)
@@ -81,3 +81,69 @@ void __uses_jump_to_uncached local_flush_tlb_one(unsigned long asid,
        ctrl_outl(data, addr);
        back_to_cached();
 }
+
+/*
+ * Load the entry for 'addr' into the TLB and wire the entry.
+ */
+void tlb_wire_entry(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
+{
+       unsigned long status, flags;
+       int urb;
+
+       local_irq_save(flags);
+
+       /* Load the entry into the TLB */
+       __update_tlb(vma, addr, pte);
+
+       /* ... and wire it up. */
+       status = ctrl_inl(MMUCR);
+       urb = (status & MMUCR_URB) >> MMUCR_URB_SHIFT;
+       status &= ~MMUCR_URB;
+
+       /*
+        * Make sure we're not trying to wire the last TLB entry slot.
+        */
+       BUG_ON(!--urb);
+
+       urb = urb % MMUCR_URB_NENTRIES;
+
+       status |= (urb << MMUCR_URB_SHIFT);
+       ctrl_outl(status, MMUCR);
+       ctrl_barrier();
+
+       local_irq_restore(flags);
+}
+
+/*
+ * Unwire the last wired TLB entry.
+ *
+ * It should also be noted that it is not possible to wire and unwire
+ * TLB entries in an arbitrary order. If you wire TLB entry N, followed
+ * by entry N+1, you must unwire entry N+1 first, then entry N. In this
+ * respect, it works like a stack or LIFO queue.
+ */
+void tlb_unwire_entry(void)
+{
+       unsigned long status, flags;
+       int urb;
+
+       local_irq_save(flags);
+
+       status = ctrl_inl(MMUCR);
+       urb = (status & MMUCR_URB) >> MMUCR_URB_SHIFT;
+       status &= ~MMUCR_URB;
+
+       /*
+        * Make sure we're not trying to unwire a TLB entry when none
+        * have been wired.
+        */
+       BUG_ON(urb++ == MMUCR_URB_NENTRIES);
+
+       urb = urb % MMUCR_URB_NENTRIES;
+
+       status |= (urb << MMUCR_URB_SHIFT);
+       ctrl_outl(status, MMUCR);
+       ctrl_barrier();
+
+       local_irq_restore(flags);
+}