aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/cpu/sh4
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/kernel/cpu/sh4')
-rw-r--r--arch/sh/kernel/cpu/sh4/Makefile10
-rw-r--r--arch/sh/kernel/cpu/sh4/ex.S384
-rw-r--r--arch/sh/kernel/cpu/sh4/fpu.c335
-rw-r--r--arch/sh/kernel/cpu/sh4/irq_intc2.c222
-rw-r--r--arch/sh/kernel/cpu/sh4/probe.c138
-rw-r--r--arch/sh/kernel/cpu/sh4/sq.c453
6 files changed, 1542 insertions, 0 deletions
diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile
new file mode 100644
index 000000000000..ead1071eac73
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the Linux/SuperH SH-4 backends.
+#
+
+obj-y := ex.o probe.o
+
+obj-$(CONFIG_SH_FPU) += fpu.o
+obj-$(CONFIG_CPU_SUBTYPE_ST40STB1) += irq_intc2.o
+obj-$(CONFIG_SH_STORE_QUEUES) += sq.o
+
diff --git a/arch/sh/kernel/cpu/sh4/ex.S b/arch/sh/kernel/cpu/sh4/ex.S
new file mode 100644
index 000000000000..8221e9d15515
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/ex.S
@@ -0,0 +1,384 @@
+/*
+ * arch/sh/kernel/cpu/sh4/ex.S
+ *
+ * The SH-4 exception vector table.
+
+ * Copyright (C) 1999, 2000, 2002 Niibe Yutaka
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * 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.
+ *
+ */
+#include <linux/linkage.h>
+#include <linux/config.h>
+
+ .align 2
+ .data
+
+ENTRY(exception_handling_table)
+ .long exception_error /* 000 */
+ .long exception_error
+#if defined(CONFIG_MMU)
+ .long tlb_miss_load /* 040 */
+ .long tlb_miss_store
+ .long initial_page_write
+ .long tlb_protection_violation_load
+ .long tlb_protection_violation_store
+ .long address_error_load
+ .long address_error_store /* 100 */
+#else
+ .long exception_error ! tlb miss load /* 040 */
+ .long exception_error ! tlb miss store
+ .long exception_error ! initial page write
+ .long exception_error ! tlb prot violation load
+ .long exception_error ! tlb prot violation store
+ .long exception_error ! address error load
+ .long exception_error ! address error store /* 100 */
+#endif
+#if defined(CONFIG_SH_FPU)
+ .long do_fpu_error /* 120 */
+#else
+ .long exception_error /* 120 */
+#endif
+ .long exception_error /* 140 */
+ .long system_call ! Unconditional Trap /* 160 */
+ .long exception_error ! reserved_instruction (filled by trap_init) /* 180 */
+ .long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/
+ENTRY(nmi_slot)
+#if defined (CONFIG_KGDB_NMI)
+ .long debug_enter /* 1C0 */ ! Allow trap to debugger
+#else
+ .long exception_none /* 1C0 */ ! Not implemented yet
+#endif
+ENTRY(user_break_point_trap)
+ .long break_point_trap /* 1E0 */
+ENTRY(interrupt_table)
+ ! external hardware
+ .long do_IRQ ! 0000 /* 200 */
+ .long do_IRQ ! 0001
+ .long do_IRQ ! 0010
+ .long do_IRQ ! 0011
+ .long do_IRQ ! 0100
+ .long do_IRQ ! 0101
+ .long do_IRQ ! 0110
+ .long do_IRQ ! 0111
+ .long do_IRQ ! 1000 /* 300 */
+ .long do_IRQ ! 1001
+ .long do_IRQ ! 1010
+ .long do_IRQ ! 1011
+ .long do_IRQ ! 1100
+ .long do_IRQ ! 1101
+ .long do_IRQ ! 1110
+ .long exception_error
+ ! Internal hardware
+ .long do_IRQ ! TMU0 tuni0 /* 400 */
+ .long do_IRQ ! TMU1 tuni1
+ .long do_IRQ ! TMU2 tuni2
+ .long do_IRQ ! ticpi2
+#if defined(CONFIG_CPU_SUBTYPE_SH7760)
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error /* 500 */
+ .long exception_error
+ .long exception_error
+#else
+ .long do_IRQ ! RTC ati
+ .long do_IRQ ! pri
+ .long do_IRQ ! cui
+ .long do_IRQ ! SCI eri
+ .long do_IRQ ! rxi /* 500 */
+ .long do_IRQ ! txi
+ .long do_IRQ ! tei
+#endif
+ .long do_IRQ ! WDT iti /* 560 */
+ .long do_IRQ ! REF rcmi
+ .long do_IRQ ! rovi
+ .long do_IRQ
+ .long do_IRQ /* 5E0 */
+ .long do_IRQ ! 32 Hitachi UDI /* 600 */
+ .long do_IRQ ! 33 GPIO
+ .long do_IRQ ! 34 DMAC dmte0
+ .long do_IRQ ! 35 dmte1
+ .long do_IRQ ! 36 dmte2
+ .long do_IRQ ! 37 dmte3
+ .long do_IRQ ! 38 dmae
+ .long exception_error ! 39 /* 6E0 */
+#if defined(CONFIG_CPU_SUBTYPE_SH7760)
+ .long exception_error /* 700 */
+ .long exception_error
+ .long exception_error
+ .long exception_error /* 760 */
+#else
+ .long do_IRQ ! 40 SCIF eri /* 700 */
+ .long do_IRQ ! 41 rxi
+ .long do_IRQ ! 42 bri
+ .long do_IRQ ! 43 txi
+#endif
+#if CONFIG_NR_ONCHIP_DMA_CHANNELS == 8
+ .long do_IRQ ! 44 DMAC dmte4 /* 780 */
+ .long do_IRQ ! 45 dmte5
+ .long do_IRQ ! 46 dmte6
+ .long do_IRQ ! 47 dmte7 /* 7E0 */
+#else
+ .long exception_error ! 44 /* 780 */
+ .long exception_error ! 45
+ .long exception_error ! 46
+ .long exception_error ! 47
+#endif
+#if defined(CONFIG_SH_FPU)
+ .long do_fpu_state_restore ! 48 /* 800 */
+ .long do_fpu_state_restore ! 49 /* 820 */
+#else
+ .long exception_error
+ .long exception_error
+#endif
+#if defined(CONFIG_CPU_SUBTYPE_SH7751)
+ .long exception_error /* 840 */
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error /* 900 */
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! PCI serr /* A00 */
+ .long do_IRQ ! dma3
+ .long do_IRQ ! dma2
+ .long do_IRQ ! dma1
+ .long do_IRQ ! dma0
+ .long do_IRQ ! pwon
+ .long do_IRQ ! pwdwn
+ .long do_IRQ ! err
+ .long do_IRQ ! TMU3 tuni3 /* B00 */
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! TMU4 tuni4 /* B80 */
+#elif defined(CONFIG_CPU_SUBTYPE_SH7760)
+ .long do_IRQ ! IRQ irq6 /* 840 */
+ .long do_IRQ ! irq7
+ .long do_IRQ ! SCIF eri0
+ .long do_IRQ ! rxi0
+ .long do_IRQ ! bri0
+ .long do_IRQ ! txi0
+ .long do_IRQ ! HCAN2 cani0 /* 900 */
+ .long do_IRQ ! cani1
+ .long do_IRQ ! SSI ssii0
+ .long do_IRQ ! ssii1
+ .long do_IRQ ! HAC haci0
+ .long do_IRQ ! haci1
+ .long do_IRQ ! IIC iici0
+ .long do_IRQ ! iici1
+ .long do_IRQ ! USB usbi /* A00 */
+ .long do_IRQ ! LCDC vint
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! DMABRG dmabrgi0
+ .long do_IRQ ! dmabrgi1
+ .long do_IRQ ! dmabrgi2
+ .long exception_error
+ .long do_IRQ ! SCIF eri1 /* B00 */
+ .long do_IRQ ! rxi1
+ .long do_IRQ ! bri1
+ .long do_IRQ ! txi1
+ .long do_IRQ ! eri2
+ .long do_IRQ ! rxi2
+ .long do_IRQ ! bri2
+ .long do_IRQ ! txi2
+ .long do_IRQ ! SIM simeri /* C00 */
+ .long do_IRQ ! simrxi
+ .long do_IRQ ! simtxi
+ .long do_IRQ ! simtei
+ .long do_IRQ ! HSPI spii
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! MMCIF mmci0 /* D00 */
+ .long do_IRQ ! mmci1
+ .long do_IRQ ! mmci2
+ .long do_IRQ ! mmci3
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error /* E00 */
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! MFI mfii
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error /* F00 */
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! ADC adi
+ .long do_IRQ ! CMT cmti /* FA0 */
+#elif defined(CONFIG_CPU_SUBTYPE_SH73180)
+ .long do_IRQ ! 50 0x840
+ .long do_IRQ ! 51 0x860
+ .long do_IRQ ! 52 0x880
+ .long do_IRQ ! 53 0x8a0
+ .long do_IRQ ! 54 0x8c0
+ .long do_IRQ ! 55 0x8e0
+ .long do_IRQ ! 56 0x900
+ .long do_IRQ ! 57 0x920
+ .long do_IRQ ! 58 0x940
+ .long do_IRQ ! 59 0x960
+ .long do_IRQ ! 60 0x980
+ .long do_IRQ ! 61 0x9a0
+ .long do_IRQ ! 62 0x9c0
+ .long do_IRQ ! 63 0x9e0
+ .long do_IRQ ! 64 0xa00
+ .long do_IRQ ! 65 0xa20
+ .long do_IRQ ! 66 0xa40
+ .long do_IRQ ! 67 0xa60
+ .long do_IRQ ! 68 0xa80
+ .long do_IRQ ! 69 0xaa0
+ .long do_IRQ ! 70 0xac0
+ .long do_IRQ ! 71 0xae0
+ .long do_IRQ ! 72 0xb00
+ .long do_IRQ ! 73 0xb20
+ .long do_IRQ ! 74 0xb40
+ .long do_IRQ ! 75 0xb60
+ .long do_IRQ ! 76 0xb80
+ .long do_IRQ ! 77 0xba0
+ .long do_IRQ ! 78 0xbc0
+ .long do_IRQ ! 79 0xbe0
+ .long do_IRQ ! 80 0xc00
+ .long do_IRQ ! 81 0xc20
+ .long do_IRQ ! 82 0xc40
+ .long do_IRQ ! 83 0xc60
+ .long do_IRQ ! 84 0xc80
+ .long do_IRQ ! 85 0xca0
+ .long do_IRQ ! 86 0xcc0
+ .long do_IRQ ! 87 0xce0
+ .long do_IRQ ! 88 0xd00
+ .long do_IRQ ! 89 0xd20
+ .long do_IRQ ! 90 0xd40
+ .long do_IRQ ! 91 0xd60
+ .long do_IRQ ! 92 0xd80
+ .long do_IRQ ! 93 0xda0
+ .long do_IRQ ! 94 0xdc0
+ .long do_IRQ ! 95 0xde0
+ .long do_IRQ ! 96 0xe00
+ .long do_IRQ ! 97 0xe20
+ .long do_IRQ ! 98 0xe40
+ .long do_IRQ ! 99 0xe60
+ .long do_IRQ ! 100 0xe80
+ .long do_IRQ ! 101 0xea0
+ .long do_IRQ ! 102 0xec0
+ .long do_IRQ ! 103 0xee0
+ .long do_IRQ ! 104 0xf00
+ .long do_IRQ ! 105 0xf20
+ .long do_IRQ ! 106 0xf40
+ .long do_IRQ ! 107 0xf60
+ .long do_IRQ ! 108 0xf80
+#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
+ .long exception_error ! 50 0x840
+ .long exception_error ! 51 0x860
+ .long exception_error ! 52 0x880
+ .long exception_error ! 53 0x8a0
+ .long exception_error ! 54 0x8c0
+ .long exception_error ! 55 0x8e0
+ .long exception_error ! 56 0x900
+ .long exception_error ! 57 0x920
+ .long exception_error ! 58 0x940
+ .long exception_error ! 59 0x960
+ .long exception_error ! 60 0x980
+ .long exception_error ! 61 0x9a0
+ .long exception_error ! 62 0x9c0
+ .long exception_error ! 63 0x9e0
+ .long do_IRQ ! 64 0xa00 PCI serr
+ .long do_IRQ ! 65 0xa20 err
+ .long do_IRQ ! 66 0xa40 ad
+ .long do_IRQ ! 67 0xa60 pwr_dwn
+ .long exception_error ! 68 0xa80
+ .long exception_error ! 69 0xaa0
+ .long exception_error ! 70 0xac0
+ .long exception_error ! 71 0xae0
+ .long do_IRQ ! 72 0xb00 DMA INT0
+ .long do_IRQ ! 73 0xb20 INT1
+ .long do_IRQ ! 74 0xb40 INT2
+ .long do_IRQ ! 75 0xb60 INT3
+ .long do_IRQ ! 76 0xb80 INT4
+ .long exception_error ! 77 0xba0
+ .long do_IRQ ! 78 0xbc0 DMA ERR
+ .long exception_error ! 79 0xbe0
+ .long do_IRQ ! 80 0xc00 PIO0
+ .long do_IRQ ! 81 0xc20 PIO1
+ .long do_IRQ ! 82 0xc40 PIO2
+ .long exception_error ! 83 0xc60
+ .long exception_error ! 84 0xc80
+ .long exception_error ! 85 0xca0
+ .long exception_error ! 86 0xcc0
+ .long exception_error ! 87 0xce0
+ .long exception_error ! 88 0xd00
+ .long exception_error ! 89 0xd20
+ .long exception_error ! 90 0xd40
+ .long exception_error ! 91 0xd60
+ .long exception_error ! 92 0xd80
+ .long exception_error ! 93 0xda0
+ .long exception_error ! 94 0xdc0
+ .long exception_error ! 95 0xde0
+ .long exception_error ! 96 0xe00
+ .long exception_error ! 97 0xe20
+ .long exception_error ! 98 0xe40
+ .long exception_error ! 99 0xe60
+ .long exception_error ! 100 0xe80
+ .long exception_error ! 101 0xea0
+ .long exception_error ! 102 0xec0
+ .long exception_error ! 103 0xee0
+ .long exception_error ! 104 0xf00
+ .long exception_error ! 105 0xf20
+ .long exception_error ! 106 0xf40
+ .long exception_error ! 107 0xf60
+ .long exception_error ! 108 0xf80
+ .long exception_error ! 109 0xfa0
+ .long exception_error ! 110 0xfc0
+ .long exception_error ! 111 0xfe0
+ .long do_IRQ ! 112 0x1000 Mailbox
+ .long exception_error ! 113 0x1020
+ .long exception_error ! 114 0x1040
+ .long exception_error ! 115 0x1060
+ .long exception_error ! 116 0x1080
+ .long exception_error ! 117 0x10a0
+ .long exception_error ! 118 0x10c0
+ .long exception_error ! 119 0x10e0
+ .long exception_error ! 120 0x1100
+ .long exception_error ! 121 0x1120
+ .long exception_error ! 122 0x1140
+ .long exception_error ! 123 0x1160
+ .long exception_error ! 124 0x1180
+ .long exception_error ! 125 0x11a0
+ .long exception_error ! 126 0x11c0
+ .long exception_error ! 127 0x11e0
+ .long exception_error ! 128 0x1200
+ .long exception_error ! 129 0x1220
+ .long exception_error ! 130 0x1240
+ .long exception_error ! 131 0x1260
+ .long exception_error ! 132 0x1280
+ .long exception_error ! 133 0x12a0
+ .long exception_error ! 134 0x12c0
+ .long exception_error ! 135 0x12e0
+ .long exception_error ! 136 0x1300
+ .long exception_error ! 137 0x1320
+ .long exception_error ! 138 0x1340
+ .long exception_error ! 139 0x1360
+ .long do_IRQ ! 140 0x1380 EMPI INV_ADDR
+ .long exception_error ! 141 0x13a0
+ .long exception_error ! 142 0x13c0
+ .long exception_error ! 143 0x13e0
+#endif
+
diff --git a/arch/sh/kernel/cpu/sh4/fpu.c b/arch/sh/kernel/cpu/sh4/fpu.c
new file mode 100644
index 000000000000..f486c07e10e2
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/fpu.c
@@ -0,0 +1,335 @@
+/* $Id: fpu.c,v 1.4 2004/01/13 05:52:11 kkojima Exp $
+ *
+ * linux/arch/sh/kernel/fpu.c
+ *
+ * Save/restore floating point context for signal handlers.
+ *
+ * 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) 1999, 2000 Kaz Kojima & Niibe Yutaka
+ *
+ * FIXME! These routines can be optimized in big endian case.
+ */
+
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+
+/* The PR (precision) bit in the FP Status Register must be clear when
+ * an frchg instruction is executed, otherwise the instruction is undefined.
+ * Executing frchg with PR set causes a trap on some SH4 implementations.
+ */
+
+#define FPSCR_RCHG 0x00000000
+
+
+/*
+ * Save FPU registers onto task structure.
+ * Assume called with FPU enabled (SR.FD=0).
+ */
+void
+save_fpu(struct task_struct *tsk, struct pt_regs *regs)
+{
+ unsigned long dummy;
+
+ clear_tsk_thread_flag(tsk, TIF_USEDFPU);
+ enable_fpu();
+ asm volatile("sts.l fpul, @-%0\n\t"
+ "sts.l fpscr, @-%0\n\t"
+ "lds %2, fpscr\n\t"
+ "frchg\n\t"
+ "fmov.s fr15, @-%0\n\t"
+ "fmov.s fr14, @-%0\n\t"
+ "fmov.s fr13, @-%0\n\t"
+ "fmov.s fr12, @-%0\n\t"
+ "fmov.s fr11, @-%0\n\t"
+ "fmov.s fr10, @-%0\n\t"
+ "fmov.s fr9, @-%0\n\t"
+ "fmov.s fr8, @-%0\n\t"
+ "fmov.s fr7, @-%0\n\t"
+ "fmov.s fr6, @-%0\n\t"
+ "fmov.s fr5, @-%0\n\t"
+ "fmov.s fr4, @-%0\n\t"
+ "fmov.s fr3, @-%0\n\t"
+ "fmov.s fr2, @-%0\n\t"
+ "fmov.s fr1, @-%0\n\t"
+ "fmov.s fr0, @-%0\n\t"
+ "frchg\n\t"
+ "fmov.s fr15, @-%0\n\t"
+ "fmov.s fr14, @-%0\n\t"
+ "fmov.s fr13, @-%0\n\t"
+ "fmov.s fr12, @-%0\n\t"
+ "fmov.s fr11, @-%0\n\t"
+ "fmov.s fr10, @-%0\n\t"
+ "fmov.s fr9, @-%0\n\t"
+ "fmov.s fr8, @-%0\n\t"
+ "fmov.s fr7, @-%0\n\t"
+ "fmov.s fr6, @-%0\n\t"
+ "fmov.s fr5, @-%0\n\t"
+ "fmov.s fr4, @-%0\n\t"
+ "fmov.s fr3, @-%0\n\t"
+ "fmov.s fr2, @-%0\n\t"
+ "fmov.s fr1, @-%0\n\t"
+ "fmov.s fr0, @-%0\n\t"
+ "lds %3, fpscr\n\t"
+ : "=r" (dummy)
+ : "0" ((char *)(&tsk->thread.fpu.hard.status)),
+ "r" (FPSCR_RCHG),
+ "r" (FPSCR_INIT)
+ : "memory");
+
+ disable_fpu();
+ release_fpu(regs);
+}
+
+static void
+restore_fpu(struct task_struct *tsk)
+{
+ unsigned long dummy;
+
+ enable_fpu();
+ asm volatile("lds %2, fpscr\n\t"
+ "fmov.s @%0+, fr0\n\t"
+ "fmov.s @%0+, fr1\n\t"
+ "fmov.s @%0+, fr2\n\t"
+ "fmov.s @%0+, fr3\n\t"
+ "fmov.s @%0+, fr4\n\t"
+ "fmov.s @%0+, fr5\n\t"
+ "fmov.s @%0+, fr6\n\t"
+ "fmov.s @%0+, fr7\n\t"
+ "fmov.s @%0+, fr8\n\t"
+ "fmov.s @%0+, fr9\n\t"
+ "fmov.s @%0+, fr10\n\t"
+ "fmov.s @%0+, fr11\n\t"
+ "fmov.s @%0+, fr12\n\t"
+ "fmov.s @%0+, fr13\n\t"
+ "fmov.s @%0+, fr14\n\t"
+ "fmov.s @%0+, fr15\n\t"
+ "frchg\n\t"
+ "fmov.s @%0+, fr0\n\t"
+ "fmov.s @%0+, fr1\n\t"
+ "fmov.s @%0+, fr2\n\t"
+ "fmov.s @%0+, fr3\n\t"
+ "fmov.s @%0+, fr4\n\t"
+ "fmov.s @%0+, fr5\n\t"
+ "fmov.s @%0+, fr6\n\t"
+ "fmov.s @%0+, fr7\n\t"
+ "fmov.s @%0+, fr8\n\t"
+ "fmov.s @%0+, fr9\n\t"
+ "fmov.s @%0+, fr10\n\t"
+ "fmov.s @%0+, fr11\n\t"
+ "fmov.s @%0+, fr12\n\t"
+ "fmov.s @%0+, fr13\n\t"
+ "fmov.s @%0+, fr14\n\t"
+ "fmov.s @%0+, fr15\n\t"
+ "frchg\n\t"
+ "lds.l @%0+, fpscr\n\t"
+ "lds.l @%0+, fpul\n\t"
+ : "=r" (dummy)
+ : "0" (&tsk->thread.fpu), "r" (FPSCR_RCHG)
+ : "memory");
+ disable_fpu();
+}
+
+/*
+ * Load the FPU with signalling NANS. This bit pattern we're using
+ * has the property that no matter wether considered as single or as
+ * double precission represents signaling NANS.
+ */
+
+static void
+fpu_init(void)
+{
+ enable_fpu();
+ asm volatile("lds %0, fpul\n\t"
+ "lds %1, fpscr\n\t"
+ "fsts fpul, fr0\n\t"
+ "fsts fpul, fr1\n\t"
+ "fsts fpul, fr2\n\t"
+ "fsts fpul, fr3\n\t"
+ "fsts fpul, fr4\n\t"
+ "fsts fpul, fr5\n\t"
+ "fsts fpul, fr6\n\t"
+ "fsts fpul, fr7\n\t"
+ "fsts fpul, fr8\n\t"
+ "fsts fpul, fr9\n\t"
+ "fsts fpul, fr10\n\t"
+ "fsts fpul, fr11\n\t"
+ "fsts fpul, fr12\n\t"
+ "fsts fpul, fr13\n\t"
+ "fsts fpul, fr14\n\t"
+ "fsts fpul, fr15\n\t"
+ "frchg\n\t"
+ "fsts fpul, fr0\n\t"
+ "fsts fpul, fr1\n\t"
+ "fsts fpul, fr2\n\t"
+ "fsts fpul, fr3\n\t"
+ "fsts fpul, fr4\n\t"
+ "fsts fpul, fr5\n\t"
+ "fsts fpul, fr6\n\t"
+ "fsts fpul, fr7\n\t"
+ "fsts fpul, fr8\n\t"
+ "fsts fpul, fr9\n\t"
+ "fsts fpul, fr10\n\t"
+ "fsts fpul, fr11\n\t"
+ "fsts fpul, fr12\n\t"
+ "fsts fpul, fr13\n\t"
+ "fsts fpul, fr14\n\t"
+ "fsts fpul, fr15\n\t"
+ "frchg\n\t"
+ "lds %2, fpscr\n\t"
+ : /* no output */
+ : "r" (0), "r" (FPSCR_RCHG), "r" (FPSCR_INIT));
+ disable_fpu();
+}
+
+/**
+ * denormal_to_double - Given denormalized float number,
+ * store double float
+ *
+ * @fpu: Pointer to sh_fpu_hard structure
+ * @n: Index to FP register
+ */
+static void
+denormal_to_double (struct sh_fpu_hard_struct *fpu, int n)
+{
+ unsigned long du, dl;
+ unsigned long x = fpu->fpul;
+ int exp = 1023 - 126;
+
+ if (x != 0 && (x & 0x7f800000) == 0) {
+ du = (x & 0x80000000);
+ while ((x & 0x00800000) == 0) {
+ x <<= 1;
+ exp--;
+ }
+ x &= 0x007fffff;
+ du |= (exp << 20) | (x >> 3);
+ dl = x << 29;
+
+ fpu->fp_regs[n] = du;
+ fpu->fp_regs[n+1] = dl;
+ }
+}
+
+/**
+ * ieee_fpe_handler - Handle denormalized number exception
+ *
+ * @regs: Pointer to register structure
+ *
+ * Returns 1 when it's handled (should not cause exception).
+ */
+static int
+ieee_fpe_handler (struct pt_regs *regs)
+{
+ unsigned short insn = *(unsigned short *) regs->pc;
+ unsigned short finsn;
+ unsigned long nextpc;
+ int nib[4] = {
+ (insn >> 12) & 0xf,
+ (insn >> 8) & 0xf,
+ (insn >> 4) & 0xf,
+ insn & 0xf};
+
+ if (nib[0] == 0xb ||
+ (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb)) /* bsr & jsr */
+ regs->pr = regs->pc + 4;
+
+ if (nib[0] == 0xa || nib[0] == 0xb) { /* bra & bsr */
+ nextpc = regs->pc + 4 + ((short) ((insn & 0xfff) << 4) >> 3);
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else if (nib[0] == 0x8 && nib[1] == 0xd) { /* bt/s */
+ if (regs->sr & 1)
+ nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1);
+ else
+ nextpc = regs->pc + 4;
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else if (nib[0] == 0x8 && nib[1] == 0xf) { /* bf/s */
+ if (regs->sr & 1)
+ nextpc = regs->pc + 4;
+ else
+ nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1);
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else if (nib[0] == 0x4 && nib[3] == 0xb &&
+ (nib[2] == 0x0 || nib[2] == 0x2)) { /* jmp & jsr */
+ nextpc = regs->regs[nib[1]];
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else if (nib[0] == 0x0 && nib[3] == 0x3 &&
+ (nib[2] == 0x0 || nib[2] == 0x2)) { /* braf & bsrf */
+ nextpc = regs->pc + 4 + regs->regs[nib[1]];
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else if (insn == 0x000b) { /* rts */
+ nextpc = regs->pr;
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else {
+ nextpc = regs->pc + 2;
+ finsn = insn;
+ }
+
+ if ((finsn & 0xf1ff) == 0xf0ad) { /* fcnvsd */
+ struct task_struct *tsk = current;
+
+ save_fpu(tsk, regs);
+ if ((tsk->thread.fpu.hard.fpscr & (1 << 17))) {
+ /* FPU error */
+ denormal_to_double (&tsk->thread.fpu.hard,
+ (finsn >> 8) & 0xf);
+ tsk->thread.fpu.hard.fpscr &=
+ ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK);
+ grab_fpu(regs);
+ restore_fpu(tsk);
+ set_tsk_thread_flag(tsk, TIF_USEDFPU);
+ } else {
+ tsk->thread.trap_no = 11;
+ tsk->thread.error_code = 0;
+ force_sig(SIGFPE, tsk);
+ }
+
+ regs->pc = nextpc;
+ return 1;
+ }
+
+ return 0;
+}
+
+asmlinkage void
+do_fpu_error(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ struct task_struct *tsk = current;
+
+ if (ieee_fpe_handler (&regs))
+ return;
+
+ regs.pc += 2;
+ save_fpu(tsk, &regs);
+ tsk->thread.trap_no = 11;
+ tsk->thread.error_code = 0;
+ force_sig(SIGFPE, tsk);
+}
+
+asmlinkage void
+do_fpu_state_restore(unsigned long r4, unsigned long r5, unsigned long r6,
+ unsigned long r7, struct pt_regs regs)
+{
+ struct task_struct *tsk = current;
+
+ grab_fpu(&regs);
+ if (!user_mode(&regs)) {
+ printk(KERN_ERR "BUG: FPU is used in kernel mode.\n");
+ return;
+ }
+
+ if (used_math()) {
+ /* Using the FPU again. */
+ restore_fpu(tsk);
+ } else {
+ /* First time FPU user. */
+ fpu_init();
+ set_used_math();
+ }
+ set_tsk_thread_flag(tsk, TIF_USEDFPU);
+}
diff --git a/arch/sh/kernel/cpu/sh4/irq_intc2.c b/arch/sh/kernel/cpu/sh4/irq_intc2.c
new file mode 100644
index 000000000000..099ebbf89745
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/irq_intc2.c
@@ -0,0 +1,222 @@
+/*
+ * linux/arch/sh/kernel/irq_intc2.c
+ *
+ * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Interrupt handling for INTC2-based IRQ.
+ *
+ * These are the "new Hitachi style" interrupts, as present on the
+ * Hitachi 7751 and the STM ST40 STB1.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/machvec.h>
+
+
+struct intc2_data {
+ unsigned char msk_offset;
+ unsigned char msk_shift;
+#ifdef CONFIG_CPU_SUBTYPE_ST40
+ int (*clear_irq) (int);
+#endif
+};
+
+
+static struct intc2_data intc2_data[NR_INTC2_IRQS];
+
+static void enable_intc2_irq(unsigned int irq);
+static void disable_intc2_irq(unsigned int irq);
+
+/* shutdown is same as "disable" */
+#define shutdown_intc2_irq disable_intc2_irq
+
+static void mask_and_ack_intc2(unsigned int);
+static void end_intc2_irq(unsigned int irq);
+
+static unsigned int startup_intc2_irq(unsigned int irq)
+{
+ enable_intc2_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static struct hw_interrupt_type intc2_irq_type = {
+ "INTC2-IRQ",
+ startup_intc2_irq,
+ shutdown_intc2_irq,
+ enable_intc2_irq,
+ disable_intc2_irq,
+ mask_and_ack_intc2,
+ end_intc2_irq
+};
+
+static void disable_intc2_irq(unsigned int irq)
+{
+ int irq_offset = irq - INTC2_FIRST_IRQ;
+ int msk_shift, msk_offset;
+
+ // Sanity check
+ if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
+ return;
+
+ msk_shift = intc2_data[irq_offset].msk_shift;
+ msk_offset = intc2_data[irq_offset].msk_offset;
+
+ ctrl_outl(1<<msk_shift,
+ INTC2_BASE+INTC2_INTMSK_OFFSET+msk_offset);
+}
+
+static void enable_intc2_irq(unsigned int irq)
+{
+ int irq_offset = irq - INTC2_FIRST_IRQ;
+ int msk_shift, msk_offset;
+
+ /* Sanity check */
+ if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
+ return;
+
+ msk_shift = intc2_data[irq_offset].msk_shift;
+ msk_offset = intc2_data[irq_offset].msk_offset;
+
+ ctrl_outl(1<<msk_shift,
+ INTC2_BASE+INTC2_INTMSKCLR_OFFSET+msk_offset);
+}
+
+static void mask_and_ack_intc2(unsigned int irq)
+{
+ disable_intc2_irq(irq);
+}
+
+static void end_intc2_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_intc2_irq(irq);
+
+#ifdef CONFIG_CPU_SUBTYPE_ST40
+ if (intc2_data[irq - INTC2_FIRST_IRQ].clear_irq)
+ intc2_data[irq - INTC2_FIRST_IRQ].clear_irq (irq);
+#endif
+}
+
+/*
+ * Setup an INTC2 style interrupt.
+ * NOTE: Unlike IPR interrupts, parameters are not shifted by this code,
+ * allowing the use of the numbers straight out of the datasheet.
+ * For example:
+ * PIO1 which is INTPRI00[19,16] and INTMSK00[13]
+ * would be: ^ ^ ^ ^
+ * | | | |
+ * make_intc2_irq(84, 0, 16, 0, 13);
+ */
+void make_intc2_irq(unsigned int irq,
+ unsigned int ipr_offset, unsigned int ipr_shift,
+ unsigned int msk_offset, unsigned int msk_shift,
+ unsigned int priority)
+{
+ int irq_offset = irq - INTC2_FIRST_IRQ;
+ unsigned int flags;
+ unsigned long ipr;
+
+ if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
+ return;
+
+ disable_irq_nosync(irq);
+
+ /* Fill the data we need */
+ intc2_data[irq_offset].msk_offset = msk_offset;
+ intc2_data[irq_offset].msk_shift = msk_shift;
+#ifdef CONFIG_CPU_SUBTYPE_ST40
+ intc2_data[irq_offset].clear_irq = NULL;
+#endif
+
+ /* Set the priority level */
+ local_irq_save(flags);
+
+ ipr=ctrl_inl(INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset);
+ ipr&=~(0xf<<ipr_shift);
+ ipr|=(priority)<<ipr_shift;
+ ctrl_outl(ipr, INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset);
+
+ local_irq_restore(flags);
+
+ irq_desc[irq].handler=&intc2_irq_type;
+
+ disable_intc2_irq(irq);
+}
+
+#ifdef CONFIG_CPU_SUBTYPE_ST40
+
+struct intc2_init {
+ unsigned short irq;
+ unsigned char ipr_offset, ipr_shift;
+ unsigned char msk_offset, msk_shift;
+};
+
+static struct intc2_init intc2_init_data[] __initdata = {
+ {64, 0, 0, 0, 0}, /* PCI serr */
+ {65, 0, 4, 0, 1}, /* PCI err */
+ {66, 0, 4, 0, 2}, /* PCI ad */
+ {67, 0, 4, 0, 3}, /* PCI pwd down */
+ {72, 0, 8, 0, 5}, /* DMAC INT0 */
+ {73, 0, 8, 0, 6}, /* DMAC INT1 */
+ {74, 0, 8, 0, 7}, /* DMAC INT2 */
+ {75, 0, 8, 0, 8}, /* DMAC INT3 */
+ {76, 0, 8, 0, 9}, /* DMAC INT4 */
+ {78, 0, 8, 0, 11}, /* DMAC ERR */
+ {80, 0, 12, 0, 12}, /* PIO0 */
+ {84, 0, 16, 0, 13}, /* PIO1 */
+ {88, 0, 20, 0, 14}, /* PIO2 */
+ {112, 4, 0, 4, 0}, /* Mailbox */
+#ifdef CONFIG_CPU_SUBTYPE_ST40GX1
+ {116, 4, 4, 4, 4}, /* SSC0 */
+ {120, 4, 8, 4, 8}, /* IR Blaster */
+ {124, 4, 12, 4, 12}, /* USB host */
+ {128, 4, 16, 4, 16}, /* Video processor BLITTER */
+ {132, 4, 20, 4, 20}, /* UART0 */
+ {134, 4, 20, 4, 22}, /* UART2 */
+ {136, 4, 24, 4, 24}, /* IO_PIO0 */
+ {140, 4, 28, 4, 28}, /* EMPI */
+ {144, 8, 0, 8, 0}, /* MAFE */
+ {148, 8, 4, 8, 4}, /* PWM */
+ {152, 8, 8, 8, 8}, /* SSC1 */
+ {156, 8, 12, 8, 12}, /* IO_PIO1 */
+ {160, 8, 16, 8, 16}, /* USB target */
+ {164, 8, 20, 8, 20}, /* UART1 */
+ {168, 8, 24, 8, 24}, /* Teletext */
+ {172, 8, 28, 8, 28}, /* VideoSync VTG */
+ {173, 8, 28, 8, 29}, /* VideoSync DVP0 */
+ {174, 8, 28, 8, 30}, /* VideoSync DVP1 */
+#endif
+};
+
+void __init init_IRQ_intc2(void)
+{
+ struct intc2_init *p;
+
+ printk(KERN_ALERT "init_IRQ_intc2\n");
+
+ for (p = intc2_init_data;
+ p<intc2_init_data+ARRAY_SIZE(intc2_init_data);
+ p++) {
+ make_intc2_irq(p->irq, p->ipr_offset, p->ipr_shift,
+ p-> msk_offset, p->msk_shift, 13);
+ }
+}
+
+/* Adds a termination callback to the interrupt */
+void intc2_add_clear_irq(int irq, int (*fn)(int))
+{
+ if (irq < INTC2_FIRST_IRQ)
+ return;
+
+ intc2_data[irq - INTC2_FIRST_IRQ].clear_irq = fn;
+}
+
+#endif /* CONFIG_CPU_SUBTYPE_ST40 */
diff --git a/arch/sh/kernel/cpu/sh4/probe.c b/arch/sh/kernel/cpu/sh4/probe.c
new file mode 100644
index 000000000000..42427b79697b
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/probe.c
@@ -0,0 +1,138 @@
+/*
+ * arch/sh/kernel/cpu/sh4/probe.c
+ *
+ * CPU Subtype Probing for SH-4.
+ *
+ * Copyright (C) 2001, 2002, 2003, 2004 Paul Mundt
+ * Copyright (C) 2003 Richard Curnow
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+
+int __init detect_cpu_and_cache_system(void)
+{
+ unsigned long pvr, prr, cvr;
+ unsigned long size;
+
+ static unsigned long sizes[16] = {
+ [1] = (1 << 12),
+ [2] = (1 << 13),
+ [4] = (1 << 14),
+ [8] = (1 << 15),
+ [9] = (1 << 16)
+ };
+
+ pvr = (ctrl_inl(CCN_PVR) >> 8) & 0xffff;
+ prr = (ctrl_inl(CCN_PRR) >> 4) & 0xff;
+ cvr = (ctrl_inl(CCN_CVR));
+
+ /*
+ * Setup some sane SH-4 defaults for the icache
+ */
+ cpu_data->icache.way_incr = (1 << 13);
+ cpu_data->icache.entry_shift = 5;
+ cpu_data->icache.entry_mask = 0x1fe0;
+ cpu_data->icache.sets = 256;
+ cpu_data->icache.ways = 1;
+ cpu_data->icache.linesz = L1_CACHE_BYTES;
+
+ /*
+ * And again for the dcache ..
+ */
+ cpu_data->dcache.way_incr = (1 << 14);
+ cpu_data->dcache.entry_shift = 5;
+ cpu_data->dcache.entry_mask = 0x3fe0;
+ cpu_data->dcache.sets = 512;
+ cpu_data->dcache.ways = 1;
+ cpu_data->dcache.linesz = L1_CACHE_BYTES;
+
+ /* Set the FPU flag, virtually all SH-4's have one */
+ cpu_data->flags |= CPU_HAS_FPU;
+
+ /*
+ * Probe the underlying processor version/revision and
+ * adjust cpu_data setup accordingly.
+ */
+ switch (pvr) {
+ case 0x205:
+ cpu_data->type = CPU_SH7750;
+ cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_PERF_COUNTER;
+ break;
+ case 0x206:
+ cpu_data->type = CPU_SH7750S;
+ cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_PERF_COUNTER;
+ break;
+ case 0x1100:
+ cpu_data->type = CPU_SH7751;
+ break;
+ case 0x2000:
+ cpu_data->type = CPU_SH73180;
+ cpu_data->icache.ways = 4;
+ cpu_data->dcache.ways = 4;
+ cpu_data->flags &= ~CPU_HAS_FPU;
+ break;
+ case 0x8000:
+ cpu_data->type = CPU_ST40RA;
+ break;
+ case 0x8100:
+ cpu_data->type = CPU_ST40GX1;
+ break;
+ case 0x700:
+ cpu_data->type = CPU_SH4_501;
+ cpu_data->icache.ways = 2;
+ cpu_data->dcache.ways = 2;
+
+ /* No FPU on the SH4-500 series.. */
+ cpu_data->flags &= ~CPU_HAS_FPU;
+ break;
+ case 0x600:
+ cpu_data->type = CPU_SH4_202;
+ cpu_data->icache.ways = 2;
+ cpu_data->dcache.ways = 2;
+ break;
+ case 0x500 ... 0x501:
+ switch (prr) {
+ case 0x10: cpu_data->type = CPU_SH7750R; break;
+ case 0x11: cpu_data->type = CPU_SH7751R; break;
+ case 0x50: cpu_data->type = CPU_SH7760; break;
+ }
+
+ cpu_data->icache.ways = 2;
+ cpu_data->dcache.ways = 2;
+
+ break;
+ default:
+ cpu_data->type = CPU_SH_NONE;
+ break;
+ }
+
+ /*
+ * On anything that's not a direct-mapped cache, look to the CVR
+ * for I/D-cache specifics.
+ */
+ if (cpu_data->icache.ways > 1) {
+ size = sizes[(cvr >> 20) & 0xf];
+ cpu_data->icache.way_incr = (size >> 1);
+ cpu_data->icache.sets = (size >> 6);
+ cpu_data->icache.entry_mask =
+ (cpu_data->icache.way_incr - (1 << 5));
+ }
+
+ if (cpu_data->dcache.ways > 1) {
+ size = sizes[(cvr >> 16) & 0xf];
+ cpu_data->dcache.way_incr = (size >> 1);
+ cpu_data->dcache.sets = (size >> 6);
+ cpu_data->dcache.entry_mask =
+ (cpu_data->dcache.way_incr - (1 << 5));
+ }
+
+ return 0;
+}
+
diff --git a/arch/sh/kernel/cpu/sh4/sq.c b/arch/sh/kernel/cpu/sh4/sq.c
new file mode 100644
index 000000000000..8437ea7430fe
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/sq.c
@@ -0,0 +1,453 @@
+/*
+ * arch/sh/kernel/cpu/sq.c
+ *
+ * General management API for SH-4 integrated Store Queues
+ *
+ * Copyright (C) 2001, 2002, 2003, 2004 Paul Mundt
+ * Copyright (C) 2001, 2002 M. R. Brown
+ *
+ * Some of this code has been adopted directly from the old arch/sh/mm/sq.c
+ * hack that was part of the LinuxDC project. For all intents and purposes,
+ * this is a completely new interface that really doesn't have much in common
+ * with the old zone-based approach at all. In fact, it's only listed here for
+ * general completeness.
+ *
+ * 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.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/miscdevice.h>
+#include <linux/vmalloc.h>
+
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/mmu_context.h>
+#include <asm/cpu/sq.h>
+
+static LIST_HEAD(sq_mapping_list);
+static DEFINE_SPINLOCK(sq_mapping_lock);
+
+/**
+ * sq_flush - Flush (prefetch) the store queue cache
+ * @addr: the store queue address to flush
+ *
+ * Executes a prefetch instruction on the specified store queue cache,
+ * so that the cached data is written to physical memory.
+ */
+inline void sq_flush(void *addr)
+{
+ __asm__ __volatile__ ("pref @%0" : : "r" (addr) : "memory");
+}
+
+/**
+ * sq_flush_range - Flush (prefetch) a specific SQ range
+ * @start: the store queue address to start flushing from
+ * @len: the length to flush
+ *
+ * Flushes the store queue cache from @start to @start + @len in a
+ * linear fashion.
+ */
+void sq_flush_range(unsigned long start, unsigned int len)
+{
+ volatile unsigned long *sq = (unsigned long *)start;
+ unsigned long dummy;
+
+ /* Flush the queues */
+ for (len >>= 5; len--; sq += 8)
+ sq_flush((void *)sq);
+
+ /* Wait for completion */
+ dummy = ctrl_inl(P4SEG_STORE_QUE);
+
+ ctrl_outl(0, P4SEG_STORE_QUE + 0);
+ ctrl_outl(0, P4SEG_STORE_QUE + 8);
+}
+
+static struct sq_mapping *__sq_alloc_mapping(unsigned long virt, unsigned long phys, unsigned long size, const char *name)
+{
+ struct sq_mapping *map;
+
+ if (virt + size > SQ_ADDRMAX)
+ return ERR_PTR(-ENOSPC);
+
+ map = kmalloc(sizeof(struct sq_mapping), GFP_KERNEL);
+ if (!map)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&map->list);
+
+ map->sq_addr = virt;
+ map->addr = phys;
+ map->size = size + 1;
+ map->name = name;
+
+ list_add(&map->list, &sq_mapping_list);
+
+ return map;
+}
+
+static unsigned long __sq_get_next_addr(void)
+{
+ if (!list_empty(&sq_mapping_list)) {
+ struct list_head *pos, *tmp;
+
+ /*
+ * Read one off the list head, as it will have the highest
+ * mapped allocation. Set the next one up right above it.
+ *
+ * This is somewhat sub-optimal, as we don't look at
+ * gaps between allocations or anything lower then the
+ * highest-level allocation.
+ *
+ * However, in the interest of performance and the general
+ * lack of desire to do constant list rebalancing, we don't
+ * worry about it.
+ */
+ list_for_each_safe(pos, tmp, &sq_mapping_list) {
+ struct sq_mapping *entry;
+
+ entry = list_entry(pos, typeof(*entry), list);
+
+ return entry->sq_addr + entry->size;
+ }
+ }
+
+ return P4SEG_STORE_QUE;
+}
+
+/**
+ * __sq_remap - Perform a translation from the SQ to a phys addr
+ * @map: sq mapping containing phys and store queue addresses.
+ *
+ * Maps the store queue address specified in the mapping to the physical
+ * address specified in the mapping.
+ */
+static struct sq_mapping *__sq_remap(struct sq_mapping *map)
+{
+ unsigned long flags, pteh, ptel;
+ struct vm_struct *vma;
+ pgprot_t pgprot;
+
+ /*
+ * Without an MMU (or with it turned off), this is much more
+ * straightforward, as we can just load up each queue's QACR with
+ * the physical address appropriately masked.
+ */
+
+ ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR0);
+ ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR1);
+
+#ifdef CONFIG_MMU
+ /*
+ * With an MMU on the other hand, things are slightly more involved.
+ * Namely, we have to have a direct mapping between the SQ addr and
+ * the associated physical address in the UTLB by way of setting up
+ * a virt<->phys translation by hand. We do this by simply specifying
+ * the SQ addr in UTLB.VPN and the associated physical address in
+ * UTLB.PPN.
+ *
+ * Notably, even though this is a special case translation, and some
+ * of the configuration bits are meaningless, we're still required
+ * to have a valid ASID context in PTEH.
+ *
+ * We could also probably get by without explicitly setting PTEA, but
+ * we do it here just for good measure.
+ */
+ spin_lock_irqsave(&sq_mapping_lock, flags);
+
+ pteh = map->sq_addr;
+ ctrl_outl((pteh & MMU_VPN_MASK) | get_asid(), MMU_PTEH);
+
+ ptel = map->addr & PAGE_MASK;
+ ctrl_outl(((ptel >> 28) & 0xe) | (ptel & 0x1), MMU_PTEA);
+
+ pgprot = pgprot_noncached(PAGE_KERNEL);
+
+ ptel &= _PAGE_FLAGS_HARDWARE_MASK;
+ ptel |= pgprot_val(pgprot);
+ ctrl_outl(ptel, MMU_PTEL);
+
+ __asm__ __volatile__ ("ldtlb" : : : "memory");
+
+ spin_unlock_irqrestore(&sq_mapping_lock, flags);
+
+ /*
+ * Next, we need to map ourselves in the kernel page table, so that
+ * future accesses after a TLB flush will be handled when we take a
+ * page fault.
+ *
+ * Theoretically we could just do this directly and not worry about
+ * setting up the translation by hand ahead of time, but for the
+ * cases where we want a one-shot SQ mapping followed by a quick
+ * writeout before we hit the TLB flush, we do it anyways. This way
+ * we at least save ourselves the initial page fault overhead.
+ */
+ vma = __get_vm_area(map->size, VM_ALLOC, map->sq_addr, SQ_ADDRMAX);
+ if (!vma)
+ return ERR_PTR(-ENOMEM);
+
+ vma->phys_addr = map->addr;
+
+ if (remap_area_pages((unsigned long)vma->addr, vma->phys_addr,
+ map->size, pgprot_val(pgprot))) {
+ vunmap(vma->addr);
+ return NULL;
+ }
+#endif /* CONFIG_MMU */
+
+ return map;
+}
+
+/**
+ * sq_remap - Map a physical address through the Store Queues
+ * @phys: Physical address of mapping.
+ * @size: Length of mapping.
+ * @name: User invoking mapping.
+ *
+ * Remaps the physical address @phys through the next available store queue
+ * address of @size length. @name is logged at boot time as well as through
+ * the procfs interface.
+ *
+ * A pre-allocated and filled sq_mapping pointer is returned, and must be
+ * cleaned up with a call to sq_unmap() when the user is done with the
+ * mapping.
+ */
+struct sq_mapping *sq_remap(unsigned long phys, unsigned int size, const char *name)
+{
+ struct sq_mapping *map;
+ unsigned long virt, end;
+ unsigned int psz;
+
+ /* Don't allow wraparound or zero size */
+ end = phys + size - 1;
+ if (!size || end < phys)
+ return NULL;
+ /* Don't allow anyone to remap normal memory.. */
+ if (phys < virt_to_phys(high_memory))
+ return NULL;
+
+ phys &= PAGE_MASK;
+
+ size = PAGE_ALIGN(end + 1) - phys;
+ virt = __sq_get_next_addr();
+ psz = (size + (PAGE_SIZE - 1)) / PAGE_SIZE;
+ map = __sq_alloc_mapping(virt, phys, size, name);
+
+ printk("sqremap: %15s [%4d page%s] va 0x%08lx pa 0x%08lx\n",
+ map->name ? map->name : "???",
+ psz, psz == 1 ? " " : "s",
+ map->sq_addr, map->addr);
+
+ return __sq_remap(map);
+}
+
+/**
+ * sq_unmap - Unmap a Store Queue allocation
+ * @map: Pre-allocated Store Queue mapping.
+ *
+ * Unmaps the store queue allocation @map that was previously created by
+ * sq_remap(). Also frees up the pte that was previously inserted into
+ * the kernel page table and discards the UTLB translation.
+ */
+void sq_unmap(struct sq_mapping *map)
+{
+ if (map->sq_addr > (unsigned long)high_memory)
+ vfree((void *)(map->sq_addr & PAGE_MASK));
+
+ list_del(&map->list);
+ kfree(map);
+}
+
+/**
+ * sq_clear - Clear a store queue range
+ * @addr: Address to start clearing from.
+ * @len: Length to clear.
+ *
+ * A quick zero-fill implementation for clearing out memory that has been
+ * remapped through the store queues.
+ */
+void sq_clear(unsigned long addr, unsigned int len)
+{
+ int i;
+
+ /* Clear out both queues linearly */
+ for (i = 0; i < 8; i++) {
+ ctrl_outl(0, addr + i + 0);
+ ctrl_outl(0, addr + i + 8);
+ }
+
+ sq_flush_range(addr, len);
+}
+
+/**
+ * sq_vma_unmap - Unmap a VMA range
+ * @area: VMA containing range.
+ * @addr: Start of range.
+ * @len: Length of range.
+ *
+ * Searches the sq_mapping_list for a mapping matching the sq addr @addr,
+ * and subsequently frees up the entry. Further cleanup is done by generic
+ * code.
+ */
+static void sq_vma_unmap(struct vm_area_struct *area,
+ unsigned long addr, size_t len)
+{
+ struct list_head *pos, *tmp;
+
+ list_for_each_safe(pos, tmp, &sq_mapping_list) {
+ struct sq_mapping *entry;
+
+ entry = list_entry(pos, typeof(*entry), list);
+
+ if (entry->sq_addr == addr) {
+ /*
+ * We could probably get away without doing the tlb flush
+ * here, as generic code should take care of most of this
+ * when unmapping the rest of the VMA range for us. Leave
+ * it in for added sanity for the time being..
+ */
+ __flush_tlb_page(get_asid(), entry->sq_addr & PAGE_MASK);
+
+ list_del(&entry->list);
+ kfree(entry);
+
+ return;
+ }
+ }
+}
+
+/**
+ * sq_vma_sync - Sync a VMA range
+ * @area: VMA containing range.
+ * @start: Start of range.
+ * @len: Length of range.
+ * @flags: Additional flags.
+ *
+ * Synchronizes an sq mapped range by flushing the store queue cache for
+ * the duration of the mapping.
+ *
+ * Used internally for user mappings, which must use msync() to prefetch
+ * the store queue cache.
+ */
+static int sq_vma_sync(struct vm_area_struct *area,
+ unsigned long start, size_t len, unsigned int flags)
+{
+ sq_flush_range(start, len);
+
+ return 0;
+}
+
+static struct vm_operations_struct sq_vma_ops = {
+ .unmap = sq_vma_unmap,
+ .sync = sq_vma_sync,
+};
+
+/**
+ * sq_mmap - mmap() for /dev/cpu/sq
+ * @file: unused.
+ * @vma: VMA to remap.
+ *
+ * Remap the specified vma @vma through the store queues, and setup associated
+ * information for the new mapping. Also build up the page tables for the new
+ * area.
+ */
+static int sq_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ struct sq_mapping *map;
+
+ /*
+ * We're not interested in any arbitrary virtual address that has
+ * been stuck in the VMA, as we already know what addresses we
+ * want. Save off the size, and reposition the VMA to begin at
+ * the next available sq address.
+ */
+ vma->vm_start = __sq_get_next_addr();
+ vma->vm_end = vma->vm_start + size;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ map = __sq_alloc_mapping(vma->vm_start, offset, size, "Userspace");
+
+ if (io_remap_pfn_range(vma, map->sq_addr, map->addr >> PAGE_SHIFT,
+ size, vma->vm_page_prot))
+ return -EAGAIN;
+
+ vma->vm_ops = &sq_vma_ops;
+
+ return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+static int sq_mapping_read_proc(char *buf, char **start, off_t off,
+ int len, int *eof, void *data)
+{
+ struct list_head *pos;
+ char *p = buf;
+
+ list_for_each_prev(pos, &sq_mapping_list) {
+ struct sq_mapping *entry;
+
+ entry = list_entry(pos, typeof(*entry), list);
+
+ p += sprintf(p, "%08lx-%08lx [%08lx]: %s\n", entry->sq_addr,
+ entry->sq_addr + entry->size - 1, entry->addr,
+ entry->name);
+ }
+
+ return p - buf;
+}
+#endif
+
+static struct file_operations sq_fops = {
+ .owner = THIS_MODULE,
+ .mmap = sq_mmap,
+};
+
+static struct miscdevice sq_dev = {
+ .minor = STORE_QUEUE_MINOR,
+ .name = "sq",
+ .devfs_name = "cpu/sq",
+ .fops = &sq_fops,
+};
+
+static int __init sq_api_init(void)
+{
+ printk(KERN_NOTICE "sq: Registering store queue API.\n");
+
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry("sq_mapping", 0, 0, sq_mapping_read_proc, 0);
+#endif
+
+ return misc_register(&sq_dev);
+}
+
+static void __exit sq_api_exit(void)
+{
+ misc_deregister(&sq_dev);
+}
+
+module_init(sq_api_init);
+module_exit(sq_api_exit);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, M. R. Brown <mrbrown@0xd6.org>");
+MODULE_DESCRIPTION("Simple API for SH-4 integrated Store Queues");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(STORE_QUEUE_MINOR);
+
+EXPORT_SYMBOL(sq_remap);
+EXPORT_SYMBOL(sq_unmap);
+EXPORT_SYMBOL(sq_clear);
+EXPORT_SYMBOL(sq_flush);
+EXPORT_SYMBOL(sq_flush_range);
+