summaryrefslogtreecommitdiffstats
path: root/arch/mips/mips4
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/mips4')
-rw-r--r--arch/mips/mips4/Makefile21
-rw-r--r--arch/mips/mips4/README3
-rw-r--r--arch/mips/mips4/cpu.c106
-rw-r--r--arch/mips/mips4/pagetables.c122
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);
+}