summaryrefslogtreecommitdiffstats
path: root/include/asm-parisc/pgalloc.h
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-01-10 05:27:25 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-01-10 05:27:25 +0000
commitc9c06167e7933d93a6e396174c68abf242294abb (patch)
treed9a8bb30663e9a3405a1ef37ffb62bc14b9f019f /include/asm-parisc/pgalloc.h
parentf79e8cc3c34e4192a3e5ef4cc9c6542fdef703c0 (diff)
Merge with Linux 2.4.0-test12.
Diffstat (limited to 'include/asm-parisc/pgalloc.h')
-rw-r--r--include/asm-parisc/pgalloc.h404
1 files changed, 404 insertions, 0 deletions
diff --git a/include/asm-parisc/pgalloc.h b/include/asm-parisc/pgalloc.h
new file mode 100644
index 000000000..1d9365252
--- /dev/null
+++ b/include/asm-parisc/pgalloc.h
@@ -0,0 +1,404 @@
+#ifndef _ASM_PGALLOC_H
+#define _ASM_PGALLOC_H
+
+/* The usual comment is "Caches aren't brain-dead on the <architecture>".
+ * Unfortunately, that doesn't apply to PA-RISC. */
+
+#include <asm/processor.h>
+#include <asm/fixmap.h>
+#include <linux/threads.h>
+
+#include <asm/pgtable.h>
+#include <asm/cache.h>
+
+
+/* Internal use D/I cache flushing routines... */
+/* XXX: these functions must not access memory between f[di]ce instructions. */
+
+static inline void __flush_dcache_range(unsigned long start, unsigned long size)
+{
+#if 0
+ register unsigned long count = (size / L1_CACHE_BYTES);
+ register unsigned long loop = cache_info.dc_loop;
+ register unsigned long i, j;
+
+ if (size > 64 * 1024) {
+ /* Just punt and clear the whole damn thing */
+ flush_data_cache();
+ return;
+ }
+
+ for(i = 0; i <= count; i++, start += L1_CACHE_BYTES)
+ for(j = 0; j < loop; j++)
+ fdce(start);
+#else
+ flush_data_cache();
+#endif
+}
+
+
+static inline void __flush_icache_range(unsigned long start, unsigned long size)
+{
+#if 0
+ register unsigned long count = (size / L1_CACHE_BYTES);
+ register unsigned long loop = cache_info.ic_loop;
+ register unsigned long i, j;
+
+ if (size > 64 * 1024) {
+ /* Just punt and clear the whole damn thing */
+ flush_instruction_cache();
+ return;
+ }
+
+ for(i = 0; i <= count; i++, start += L1_CACHE_BYTES)
+ for(j = 0; j < loop; j++)
+ fice(start);
+#else
+ flush_instruction_cache();
+#endif
+}
+
+static inline void
+flush_kernel_dcache_range(unsigned long start, unsigned long size)
+{
+ register unsigned long end = start + size;
+ register unsigned long i;
+
+ start &= ~(L1_CACHE_BYTES - 1);
+ for (i = start; i < end; i += L1_CACHE_BYTES) {
+ kernel_fdc(i);
+ }
+ asm volatile("sync" : : );
+ asm volatile("syncdma" : : );
+}
+
+extern void __flush_page_to_ram(unsigned long address);
+
+#define flush_cache_all() flush_all_caches()
+#define flush_cache_mm(foo) flush_all_caches()
+
+#if 0
+/* This is how I think the cache flushing should be done -- mrw */
+extern inline void flush_cache_mm(struct mm_struct *mm) {
+ if (mm == current->mm) {
+ flush_user_dcache_range(mm->start_data, mm->end_data);
+ flush_user_icache_range(mm->start_code, mm->end_code);
+ } else {
+ flush_other_dcache_range(mm->context, mm->start_data, mm->end_data);
+ flush_other_icache_range(mm->context, mm->start_code, mm->end_code);
+ }
+}
+#endif
+
+#define flush_cache_range(mm, start, end) do { \
+ __flush_dcache_range(start, (unsigned long)end - (unsigned long)start); \
+ __flush_icache_range(start, (unsigned long)end - (unsigned long)start); \
+} while(0)
+
+#define flush_cache_page(vma, vmaddr) do { \
+ __flush_dcache_range(vmaddr, PAGE_SIZE); \
+ __flush_icache_range(vmaddr, PAGE_SIZE); \
+} while(0)
+
+#define flush_page_to_ram(page) \
+ __flush_page_to_ram((unsigned long)page_address(page))
+
+#define flush_icache_range(start, end) \
+ __flush_icache_range(start, end - start)
+
+#define flush_icache_page(vma, page) \
+ __flush_icache_range(page_address(page), PAGE_SIZE)
+
+#define flush_dcache_page(page) \
+ __flush_dcache_range(page_address(page), PAGE_SIZE)
+
+/* TLB flushing routines.... */
+
+extern void flush_data_tlb(void);
+extern void flush_instruction_tlb(void);
+
+#define flush_tlb() do { \
+ flush_data_tlb(); \
+ flush_instruction_tlb(); \
+} while(0);
+
+#define flush_tlb_all() flush_tlb() /* XXX p[id]tlb */
+
+extern __inline__ void flush_tlb_pgtables(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+}
+
+static inline void flush_instruction_tlb_range(unsigned long start,
+ unsigned long size)
+{
+#if 0
+ register unsigned long count = (size / PAGE_SIZE);
+ register unsigned long loop = cache_info.it_loop;
+ register unsigned long i, j;
+
+ for(i = 0; i <= count; i++, start += PAGE_SIZE)
+ for(j = 0; j < loop; j++)
+ pitlbe(start);
+#else
+ flush_instruction_tlb();
+#endif
+}
+
+static inline void flush_data_tlb_range(unsigned long start,
+ unsigned long size)
+{
+#if 0
+ register unsigned long count = (size / PAGE_SIZE);
+ register unsigned long loop = cache_info.dt_loop;
+ register unsigned long i, j;
+
+ for(i = 0; i <= count; i++, start += PAGE_SIZE)
+ for(j = 0; j < loop; j++)
+ pdtlbe(start);
+#else
+ flush_data_tlb();
+#endif
+}
+
+
+
+static inline void __flush_tlb_range(unsigned long space, unsigned long start,
+ unsigned long size)
+{
+ unsigned long old_sr1;
+
+ if(!size)
+ return;
+
+ old_sr1 = mfsp(1);
+ mtsp(space, 1);
+
+ flush_data_tlb_range(start, size);
+ flush_instruction_tlb_range(start, size);
+
+ mtsp(old_sr1, 1);
+}
+
+extern void __flush_tlb_space(unsigned long space);
+
+static inline void flush_tlb_mm(struct mm_struct *mm)
+{
+#if 0
+ __flush_tlb_space(mm->context);
+#else
+ flush_tlb();
+#endif
+}
+
+static inline void flush_tlb_page(struct vm_area_struct *vma,
+ unsigned long addr)
+{
+ __flush_tlb_range(vma->vm_mm->context, addr, PAGE_SIZE);
+
+}
+
+static inline void flush_tlb_range(struct mm_struct *mm,
+ unsigned long start, unsigned long end)
+{
+ __flush_tlb_range(mm->context, start, end - start);
+}
+
+/*
+ * NOTE: Many of the below macros use PT_NLEVELS because
+ * it is convenient that PT_NLEVELS == LOG2(pte size in bytes),
+ * i.e. we use 3 level page tables when we use 8 byte pte's
+ * (for 64 bit) and 2 level page tables when we use 4 byte pte's
+ */
+
+#ifdef __LP64__
+#define PT_NLEVELS 3
+#define PT_INITIAL 4 /* Number of initial page tables */
+#else
+#define PT_NLEVELS 2
+#define PT_INITIAL 2 /* Number of initial page tables */
+#endif
+
+/* Definitions for 1st level */
+
+#define PGDIR_SHIFT (PAGE_SHIFT + (PT_NLEVELS - 1)*(PAGE_SHIFT - PT_NLEVELS))
+#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK (~(PGDIR_SIZE-1))
+#define PTRS_PER_PGD (1UL << (PAGE_SHIFT - PT_NLEVELS))
+#define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE)
+
+/* Definitions for 2nd level */
+
+#define PMD_SHIFT (PAGE_SHIFT + (PAGE_SHIFT - PT_NLEVELS))
+#define PMD_SIZE (1UL << PMD_SHIFT)
+#define PMD_MASK (~(PMD_SIZE-1))
+#if PT_NLEVELS == 3
+#define PTRS_PER_PMD (1UL << (PAGE_SHIFT - PT_NLEVELS))
+#else
+#define PTRS_PER_PMD 1
+#endif
+
+/* Definitions for 3rd level */
+
+#define PTRS_PER_PTE (1UL << (PAGE_SHIFT - PT_NLEVELS))
+
+
+#define get_pgd_fast get_pgd_slow
+#define free_pgd_fast free_pgd_slow
+
+extern __inline__ pgd_t *get_pgd_slow(void)
+{
+ extern unsigned long gateway_pgd_offset;
+ extern unsigned long gateway_pgd_entry;
+ pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL);
+
+ if (ret) {
+ memset (ret, 0, PTRS_PER_PGD * sizeof(pgd_t));
+
+ /* Install HP-UX and Linux gateway page translations */
+
+ pgd_val(*(ret + gateway_pgd_offset)) = gateway_pgd_entry;
+ }
+ return ret;
+}
+
+extern __inline__ void free_pgd_slow(pgd_t *pgd)
+{
+ free_page((unsigned long)pgd);
+}
+
+#if PT_NLEVELS == 3
+
+/* Three Level Page Table Support for pmd's */
+
+extern __inline__ pmd_t *get_pmd_fast(void)
+{
+ return NULL; /* la la */
+}
+
+#if 0
+extern __inline__ void free_pmd_fast(pmd_t *pmd)
+{
+}
+#else
+#define free_pmd_fast free_pmd_slow
+#endif
+
+extern __inline__ pmd_t *get_pmd_slow(void)
+{
+ pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL);
+
+ if (pmd)
+ clear_page(pmd);
+ return pmd;
+}
+
+extern __inline__ void free_pmd_slow(pmd_t *pmd)
+{
+ free_page((unsigned long)pmd);
+}
+
+extern void __bad_pgd(pgd_t *pgd);
+
+extern inline pmd_t * pmd_alloc(pgd_t *pgd, unsigned long address)
+{
+ address = (address >> PMD_SHIFT) & (PTRS_PER_PMD - 1);
+
+ if (pgd_none(*pgd))
+ goto getnew;
+ if (pgd_bad(*pgd))
+ goto fix;
+ return (pmd_t *) pgd_page(*pgd) + address;
+getnew:
+{
+ pmd_t *page = get_pmd_fast();
+
+ if (!page)
+ page = get_pmd_slow();
+ if (page) {
+ if (pgd_none(*pgd)) {
+ pgd_val(*pgd) = _PAGE_TABLE + __pa((unsigned long)page);
+ return page + address;
+ }
+ else
+ free_pmd_fast(page);
+ }
+ else {
+ return NULL;
+ }
+}
+fix:
+ __bad_pgd(pgd);
+ return NULL;
+}
+
+#else
+
+/* Two Level Page Table Support for pmd's */
+
+extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address)
+{
+ return (pmd_t *) pgd;
+}
+
+extern inline void free_pmd_fast(pmd_t * pmd)
+{
+}
+
+#endif
+
+extern __inline__ pte_t *get_pte_fast(void)
+{
+ return NULL; /* la la */
+}
+
+#if 0
+extern __inline__ void free_pte_fast(pte_t *pte)
+{
+}
+#else
+#define free_pte_fast free_pte_slow
+#endif
+
+extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long address_preadjusted);
+
+extern __inline__ void free_pte_slow(pte_t *pte)
+{
+ free_page((unsigned long)pte);
+}
+
+#define pmd_alloc_kernel pmd_alloc
+#define pte_alloc_kernel pte_alloc
+
+#define pte_free(pte) free_pte_fast(pte)
+#define pmd_free(pmd) free_pmd_fast(pmd)
+#define pgd_free(pgd) free_pgd_fast(pgd)
+#define pgd_alloc() get_pgd_fast()
+
+extern void __bad_pmd(pmd_t *pmd);
+
+extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address)
+{
+ address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
+
+ if (pmd_none(*pmd))
+ goto getnew;
+ if (pmd_bad(*pmd))
+ goto fix;
+ return (pte_t *) pmd_page(*pmd) + address;
+getnew:
+{
+ pte_t *page = get_pte_fast();
+
+ if (!page)
+ return get_pte_slow(pmd, address);
+ pmd_val(*pmd) = _PAGE_TABLE + __pa((unsigned long)page);
+ return page + address;
+}
+fix:
+ __bad_pmd(pmd);
+ return NULL;
+}
+
+extern int do_check_pgt_cache(int, int);
+
+#endif