diff options
Diffstat (limited to 'arch/mips/mips4')
-rw-r--r-- | arch/mips/mips4/Makefile | 21 | ||||
-rw-r--r-- | arch/mips/mips4/README | 3 | ||||
-rw-r--r-- | arch/mips/mips4/cpu.c | 106 | ||||
-rw-r--r-- | arch/mips/mips4/pagetables.c | 122 |
4 files changed, 252 insertions, 0 deletions
diff --git a/arch/mips/mips4/Makefile b/arch/mips/mips4/Makefile new file mode 100644 index 000000000..d5827861b --- /dev/null +++ b/arch/mips/mips4/Makefile @@ -0,0 +1,21 @@ +# +# Makefile for the MIPS IV specific parts of the Linux/MIPS kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +all: mips.o +EXTRA_ASFLAGS = -mips4 -mcpu=r8000 +O_TARGET := mips.o +O_OBJS := cpu.o pagetables.o showregs.o + +clean: + +include $(TOPDIR)/Rules.make diff --git a/arch/mips/mips4/README b/arch/mips/mips4/README new file mode 100644 index 000000000..22e90921b --- /dev/null +++ b/arch/mips/mips4/README @@ -0,0 +1,3 @@ +This directory contains the start of the R8000/R10000 specific part. I +tried to support this CPU as good as possible without a machine and +without detailed documentation. diff --git a/arch/mips/mips4/cpu.c b/arch/mips/mips4/cpu.c new file mode 100644 index 000000000..ef5a3f8db --- /dev/null +++ b/arch/mips/mips4/cpu.c @@ -0,0 +1,106 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1996 Ralf Baechle + */ +#include <linux/sched.h> + +#include <asm/cache.h> +#include <asm/mipsregs.h> +#include <asm/page.h> +#include <asm/processor.h> + +extern asmlinkage void mips4_cacheflush(void *addr, int nbytes, unsigned int flags); + +unsigned long page_colour_mask; + +void (*mips_cache_init)(void); + +static void +mips4_cache_init(void) +{ + /* + * The R10000 is in most aspects similar to the R4400. It + * should get some special optimizations. + */ + write_32bit_cp0_register(CP0_FRAMEMASK, 0); + set_cp0_status(ST0_XX, ST0_XX); + /* + * Actually this mask stands for only 16k cache. This is + * correct since the R10000 has multiple ways in it's cache. + */ + page_colour_mask = 0x3000; + cacheflush = mips4_cacheflush; + /* + * The R10k might even work for Linux/MIPS - but we're paranoid + * and refuse to run until this is tested on real silicon + */ + panic("CPU too expensive - making holiday in the ANDES!"); +} + +void (*switch_to_user_mode)(struct pt_regs *regs); + +static void +mips4_switch_to_user_mode(struct pt_regs *regs) +{ + regs->cp0_status = (regs->cp0_status & ~(ST0_CU0|ST0_KSU)) | KSU_USER; +} + +unsigned long (*thread_saved_pc)(struct thread_struct *t); + +/* + * Return saved PC of a blocked thread. + * XXX This works only for 64 bit kernels. + */ +static unsigned long mips4_thread_saved_pc(struct thread_struct *t) +{ + return ((unsigned long long *)(unsigned long)t->reg29)[11]; +} + +unsigned long (*get_wchan)(struct task_struct *p); + +static unsigned long mips4_get_wchan(struct task_struct *p) +{ + /* + * This one depends on the frame size of schedule(). Do a + * "disass schedule" in gdb to find the frame size. Also, the + * code assumes that sleep_on() follows immediately after + * interruptible_sleep_on() and that add_timer() follows + * immediately after interruptible_sleep(). Ugly, isn't it? + * Maybe adding a wchan field to task_struct would be better, + * after all... + */ + unsigned long schedule_frame; + unsigned long pc; + + pc = thread_saved_pc(&p->tss); + if (pc >= (unsigned long) interruptible_sleep_on && pc < (unsigned long) add_timer) { + schedule_frame = ((unsigned long long *)(long)p->tss.reg30)[10]; + return (unsigned long)((unsigned long long *)schedule_frame)[9]; + } + return pc; +} + +extern void mips4_clear_page(unsigned long page); +extern void mips4_copy_page(unsigned long to, unsigned long from); +asmlinkage void (*restore_fp_context)(struct sigcontext *sc); +asmlinkage void (*save_fp_context)(struct sigcontext *sc); + +void +mips4_cpu_init(void) +{ + extern void mips4_pgd_init(unsigned long page); + extern asmlinkage void mips4_restore_fp_context(struct sigcontext *sc); + extern asmlinkage void mips4_save_fp_context(struct sigcontext *sc); + + mips_cache_init = mips4_cache_init; + pgd_init = mips1_pgd_init; + switch_to_user_mode = mips4_switch_to_user_mode; + thread_saved_pc = mips4_thread_saved_pc; + get_wchan = mips4_get_wchan; + clear_page = mips4_clear_page; + restore_fp_context = mips4_restore_fp_context; + save_fp_context = mips4_save_fp_context; +} diff --git a/arch/mips/mips4/pagetables.c b/arch/mips/mips4/pagetables.c new file mode 100644 index 000000000..b1b86290a --- /dev/null +++ b/arch/mips/mips4/pagetables.c @@ -0,0 +1,122 @@ +/* + * 64 bit MIPS specific page handling. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1996 Ralf Baechle + */ +#include <linux/mm.h> +#include <asm/cache.h> +#include <asm/mipsconfig.h> +#include <asm/page.h> +#include <asm/pgtable.h> + +void (*pgd_init)(unsigned long page); + +/* + * Initialize new page directory with pointers to invalid ptes + */ +void mips4_pgd_init(unsigned long page) +{ + unsigned long dummy1, dummy2; + + /* + * We generate dirty lines in the datacache, overwrite these lines + * with zeros and then flush the cache. Sounds horribly complicated + * but is just a trick to avoid unnecessary loads of from memory + * and uncached stores which are very expensive. + * FIXME: This is the same like the R4000 version. We could do some + * R10000 trickery using caching mode "uncached accelerated". + */ + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "dsll32\t$1,%2,0\n\t" + "dsrl32\t%2,$1,0\n\t" + "or\t%2,$1\n" + "1:\t" + "cache\t%5,(%0)\n\t" + "sd\t%2,(%0)\n\t" + "sd\t%2,8(%0)\n\t" + "cache\t%5,16(%0)\n\t" + "sd\t%2,16(%0)\n\t" + "sd\t%2,24(%0)\n\t" + "cache\t%5,32(%0)\n\t" + "sd\t%2,32(%0)\n\t" + "sd\t%2,40(%0)\n\t" + "cache\t%5,48(%0)\n\t" + "sd\t%2,48(%0)\n\t" + "sd\t%2,56(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,64\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=&r" (dummy1), + "=&r" (dummy2) + :"r" (((unsigned long) invalid_pte_table - PAGE_OFFSET) | + _PAGE_TABLE), + "0" (page), + "1" (PAGE_SIZE/(sizeof(pmd_t)*16)), + "i" (Create_Dirty_Excl_D) + :"$1"); + /* + * Now force writeback to ashure values are in the RAM. + */ + cacheflush(page, PAGE_SIZE, CF_DCACHE|CF_VIRTUAL); +} + +void (*clear_page)(unsigned long page); + +/* + * To do: cache magic + */ +void mips4_clear_page(unsigned long page) +{ + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + "daddiu\t$1,%0,%2\n" + "1:\tsd\t$0,(%0)\n\t" + "sd\t$0,8(%0)\n\t" + "sd\t$0,16(%0)\n\t" + "sd\t$0,24(%0)\n\t" + "daddiu\t%0,64\n\t" + "sd\t$0,-32(%0)\n\t" + "sd\t$0,-24(%0)\n\t" + "sd\t$0,-16(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sd\t$0,-8(%0)\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (page) + :"0" (page), + "I" (PAGE_SIZE) + :"$1","memory"); +} + +void (*copy_page)(unsigned long to, unsigned long from); + +/* + * This is horribly inefficient ... + */ +void mips4_copy_page(unsigned long to, unsigned long from) +{ + /* + * Force writeback of old page to memory. We don't know the + * virtual address, so we have to flush the entire cache ... + */ + cacheflush(0, ~0, CF_DCACHE|CF_VIRTUAL); + sync_mem(); + memcpy((void *) to, + (void *) (from + (PT_OFFSET - PAGE_OFFSET)), PAGE_SIZE); + /* + * Now writeback the page again if colour has changed. + */ + if (page_colour(from) != page_colour(to)) + cacheflush(0, ~0, CF_DCACHE|CF_VIRTUAL); +} |