Hello,
Here is an implementation of an FPU interrupt handler for R3k DECstations
(R4k ones are fine with the dedicated exception). At first I thought only
interrupt to do_fpe() glue is missing but to my surprise I discovered no
R3k-class CPU is currently assumed to feature an FPU. As making use of an
FPU is usually advantageous if one exists, I implemented an FPU detection
function following guidelines given by my MIPS manual (I can't test an
FPU-less system).
I believe the code is stable as is. It does not provide any additional
functionality beyond what is currently available to systems using the
dedicated FPU exception. To verify an FPU is detected properly (the
kernel does not report it in any way now) and FPU exceptions are delivered
successfully the following program can be used.
fdiv.c:
#include <stddef.h>
#include <stdio.h>
#include <fpu_control.h>
int main(void)
{
double x, y, z;
int r;
fpu_control_t fpcw;
unsigned int fpir;
setvbuf(stdout, NULL, _IONBF, 0);
_FPU_GETCW(fpcw);
fpcw &= _FPU_RESERVED;
fpcw |= _FPU_IEEE;
_FPU_SETCW(fpcw);
__asm__("cfc1 %0, $0"
: "=r" (fpir)
:
: "memory");
_FPU_GETCW(fpcw);
printf("FPCW: %08x\n", fpcw);
printf("FPIR: %08x\n", fpir);
__asm__(".set push\n\t"
".set reorder\n\t"
"mtc1 %z4, %1\n\t"
"mtc1 %z5, %2\n\t"
"cvt.d.w %1, %1\n\t"
"cvt.d.w %2, %2\n\t"
".set noreorder\n\t"
"b 0f\n\t"
" div.d %3, %1, %2\n\t"
".set reorder\n\t"
"nop\n"
"0:\n\t"
"cvt.w.d %3, %3\n\t"
"mfc1 %0, %3\n\t"
".set pop"
: "=r" (r), "=f" (x), "=f" (y), "=f" (z)
: "Jr" (0), "Jr" (0));
return r;
}
You should receive an output similar to one of the following ones:
FPCW: 00000f80
FPIR: 00000340
Floating point exception
for an R3k-class FPU,
FPCW: 00000f80
FPIR: 00000500
Floating point exception
for an R4k-class FPU,
FPCW: 00000f80
FPIR: 00000000
Floating point exception
for the FPU emulation (the Implementation and Revision register is zero).
If you don't get an exception then there is a problem with FPU interrupt
delivery -- please report it to me, especially if it happens for a
DECstation.
The patch includes a few minor clean-ups of nearby code as well.
Maciej
--
+ Maciej W. Rozycki, Technical University of Gdansk, Poland +
+--------------------------------------------------------------+
+ e-mail: macro@ds2.pg.gda.pl, PGP key available +
patch-mips-2.4.14-20011123-dec-fpu-9
diff -up --recursive --new-file
linux-mips-2.4.14-20011123-dist/arch/mips/dec/int-handler.S
linux-mips-2.4.14-20011123-dec-fpu/arch/mips/dec/int-handler.S
--- linux-mips-2.4.14-20011123-dist/arch/mips/dec/int-handler.S Tue Jul 3
04:27:16 2001
+++ linux-mips-2.4.14-20011123-dec-fpu/arch/mips/dec/int-handler.S Fri Nov
23 16:35:53 2001
@@ -2,7 +2,7 @@
* arch/mips/dec/int-handler.S
*
* Copyright (C) 1995, 1996, 1997 Paul M. Antoine and Harald Koerfgen
- * Copyright (C) 2000 Maciej W. Rozycki
+ * Copyright (C) 2000, 2001 Maciej W. Rozycki
*
* Written by Ralf Baechle and Andreas Busse, modified for DECStation
* support by Paul Antoine and Harald Koerfgen.
@@ -145,6 +145,9 @@
beqz t0,spurious
+ andi t2,t0,DEC_IE_FPU
+ bnez t2,fpu # handle FPU immediately
+
/*
* Find irq with highest priority
*/
@@ -169,7 +172,7 @@
* Handle "IRQ Controller" Interrupts
* Masked Interrupts are still visible and have to be masked "by hand".
*/
- EXPORT(kn02_io_int)
+ FEXPORT(kn02_io_int)
kn02_io_int: # 3max
lui t0,KN02_CSR_ADDR>>16 # get interrupt status and mask
lw t0,(t0)
@@ -179,7 +182,7 @@ kn02_io_int: # 3max
b find_int
and t0,t3 # mask out allowed ones
- EXPORT(kn03_io_int)
+ FEXPORT(kn03_io_int)
kn03_io_int: # 3max+
lui t2,KN03_IOASIC_BASE>>16 # upper part of IOASIC Address
lw t0,SIR(t2) # get status: IOASIC isr
@@ -188,7 +191,7 @@ kn03_io_int: # 3max+
b find_int
and t0,t3 # mask out allowed ones
- EXPORT(kn02xa_io_int)
+ FEXPORT(kn02xa_io_int)
kn02xa_io_int: # 3min/maxine
lui t2,KN02XA_IOASIC_BASE>>16
# upper part of IOASIC Address
@@ -219,28 +222,27 @@ handle_it: jal do_IRQ
j ret_from_irq
nop
+fpu:
+ j handle_fpe_int
+ nop
+
spurious:
j spurious_interrupt
nop
END(decstation_handle_int)
-/*
- * Interrupt routines common to all DECStations first.
- */
- EXPORT(dec_intr_fpu)
-dec_intr_fpu: PANIC("Unimplemented FPU interrupt handler")
/*
* Generic unimplemented interrupt routines - ivec_tbl is initialised to
* point all interrupts here. The table is then filled in by machine-specific
* initialisation in dec_setup().
*/
- EXPORT(dec_intr_unimplemented)
+ FEXPORT(dec_intr_unimplemented)
dec_intr_unimplemented:
mfc0 a1,CP0_CAUSE # cheats way of printing an arg!
nop # to be sure...
PANIC("Unimplemented cpu interrupt! CP0_CAUSE: 0x%x");
- EXPORT(asic_intr_unimplemented)
+ FEXPORT(asic_intr_unimplemented)
asic_intr_unimplemented:
move a1,t0 # cheats way of printing an arg!
PANIC("Unimplemented asic interrupt! ASIC ISR: 0x%x");
diff -up --recursive --new-file
linux-mips-2.4.14-20011123-dist/arch/mips/dec/setup.c
linux-mips-2.4.14-20011123-dec-fpu/arch/mips/dec/setup.c
--- linux-mips-2.4.14-20011123-dist/arch/mips/dec/setup.c Tue Jul 3
04:27:16 2001
+++ linux-mips-2.4.14-20011123-dec-fpu/arch/mips/dec/setup.c Thu Nov 22
00:20:46 2001
@@ -6,7 +6,7 @@
* for more details.
*
* Copyright (C) 1998 Harald Koerfgen
- * Copyright (C) 2000 Maciej W. Rozycki
+ * Copyright (C) 2000, 2001 Maciej W. Rozycki
*/
#include <linux/sched.h>
#include <linux/interrupt.h>
@@ -55,7 +55,7 @@ extern int setup_dec_irq(int, struct irq
void (*board_time_init) (struct irqaction * irq);
-static struct irqaction irq10 = {dec_intr_halt, 0, 0, "halt", NULL, NULL};
+static struct irqaction haltirq = {dec_intr_halt, 0, 0, "halt", NULL, NULL};
/*
* enable the periodic interrupts
@@ -139,10 +139,10 @@ void __init dec_init_kn01(void)
cpu_mask_tbl[4] = IE_IRQ4;
cpu_irq_nr[4] = MEMORY;
- dec_interrupt[FPU].cpu_mask = IE_IRQ5;
- dec_interrupt[FPU].iemask = 0;
- cpu_mask_tbl[5] = IE_IRQ5;
- cpu_irq_nr[5] = FPU;
+ /*
+ * Enable board interrupts: FPU.
+ */
+ set_cp0_status(DEC_IE_FPU);
} /* dec_init_kn01 */
/*
@@ -165,10 +165,10 @@ void __init dec_init_kn230(void)
cpu_mask_tbl[0] = IE_IRQ2;
cpu_irq_nr[0] = CLOCK;
- dec_interrupt[FPU].cpu_mask = IE_IRQ5;
- dec_interrupt[FPU].iemask = 0;
- cpu_mask_tbl[5] = IE_IRQ5;
- cpu_irq_nr[5] = FPU;
+ /*
+ * Enable board interrupts: FPU.
+ */
+ set_cp0_status(DEC_IE_FPU);
} /* dec_init_kn230 */
/*
@@ -176,6 +176,8 @@ void __init dec_init_kn230(void)
*/
void __init dec_init_kn02(void)
{
+ int dec_ie_io;
+
/*
* Setup some memory addresses. FIXME: probably incomplete!
*/
@@ -184,10 +186,11 @@ void __init dec_init_kn02(void)
imr = (void *) KN02_CSR_ADDR;
/*
- * Setup IOASIC interrupt
+ * Setup I/O interrupt
*/
+ dec_ie_io = IE_IRQ0;
cpu_ivec_tbl[1] = kn02_io_int;
- cpu_mask_tbl[1] = IE_IRQ0;
+ cpu_mask_tbl[1] = dec_ie_io;
cpu_irq_nr[1] = -1;
*imr = *imr & 0xff00ff00;
@@ -234,11 +237,10 @@ void __init dec_init_kn02(void)
cpu_mask_tbl[2] = IE_IRQ3;
cpu_irq_nr[2] = MEMORY;
- dec_interrupt[FPU].cpu_mask = IE_IRQ5;
- dec_interrupt[FPU].iemask = 0;
- cpu_mask_tbl[3] = IE_IRQ5;
- cpu_irq_nr[3] = FPU;
-
+ /*
+ * Enable board interrupts: FPU, I/O.
+ */
+ set_cp0_status(DEC_IE_FPU | dec_ie_io);
} /* dec_init_kn02 */
/*
@@ -246,6 +248,8 @@ void __init dec_init_kn02(void)
*/
void __init dec_init_kn02ba(void)
{
+ int dec_ie_ioasic;
+
/*
* Setup some memory addresses.
*/
@@ -257,9 +261,10 @@ void __init dec_init_kn02ba(void)
/*
* Setup IOASIC interrupt
*/
- cpu_mask_tbl[0] = IE_IRQ3;
- cpu_irq_nr[0] = -1;
+ dec_ie_ioasic = IE_IRQ3;
cpu_ivec_tbl[0] = kn02xa_io_int;
+ cpu_mask_tbl[0] = dec_ie_ioasic;
+ cpu_irq_nr[0] = -1;
*imr = 0;
/*
@@ -315,12 +320,12 @@ void __init dec_init_kn02ba(void)
cpu_mask_tbl[4] = IE_IRQ4;
cpu_irq_nr[4] = HALT;
- dec_interrupt[FPU].cpu_mask = IE_IRQ5;
- dec_interrupt[FPU].iemask = 0;
- cpu_mask_tbl[5] = IE_IRQ5;
- cpu_irq_nr[5] = FPU;
+ /*
+ * Enable board interrupts: FPU, I/O ASIC.
+ */
+ set_cp0_status(DEC_IE_FPU | dec_ie_ioasic);
- dec_halt_init(&irq10);
+ dec_halt_init(&haltirq);
} /* dec_init_kn02ba */
/*
@@ -328,6 +333,8 @@ void __init dec_init_kn02ba(void)
*/
void __init dec_init_kn02ca(void)
{
+ int dec_ie_ioasic;
+
/*
* Setup some memory addresses. FIXME: probably incomplete!
*/
@@ -339,9 +346,10 @@ void __init dec_init_kn02ca(void)
/*
* Setup IOASIC interrupt
*/
+ dec_ie_ioasic = IE_IRQ3;
cpu_ivec_tbl[1] = kn02xa_io_int;
+ cpu_mask_tbl[1] = dec_ie_ioasic;
cpu_irq_nr[1] = -1;
- cpu_mask_tbl[1] = IE_IRQ3;
*imr = 0;
/*
@@ -392,12 +400,12 @@ void __init dec_init_kn02ca(void)
cpu_mask_tbl[3] = IE_IRQ4;
cpu_irq_nr[3] = HALT;
- dec_interrupt[FPU].cpu_mask = IE_IRQ5;
- dec_interrupt[FPU].iemask = 0;
- cpu_mask_tbl[4] = IE_IRQ5;
- cpu_irq_nr[4] = FPU;
+ /*
+ * Enable board interrupts: FPU, I/O ASIC.
+ */
+ set_cp0_status(DEC_IE_FPU | dec_ie_ioasic);
- dec_halt_init(&irq10);
+ dec_halt_init(&haltirq);
} /* dec_init_kn02ca */
/*
@@ -405,6 +413,8 @@ void __init dec_init_kn02ca(void)
*/
void __init dec_init_kn03(void)
{
+ int dec_ie_ioasic;
+
/*
* Setup some memory addresses. FIXME: probably incomplete!
*/
@@ -416,8 +426,9 @@ void __init dec_init_kn03(void)
/*
* Setup IOASIC interrupt
*/
+ dec_ie_ioasic = IE_IRQ0;
cpu_ivec_tbl[1] = kn03_io_int;
- cpu_mask_tbl[1] = IE_IRQ0;
+ cpu_mask_tbl[1] = dec_ie_ioasic;
cpu_irq_nr[1] = -1;
*imr = 0;
@@ -474,10 +485,10 @@ void __init dec_init_kn03(void)
cpu_mask_tbl[3] = IE_IRQ4;
cpu_irq_nr[3] = HALT;
- dec_interrupt[FPU].cpu_mask = IE_IRQ5;
- dec_interrupt[FPU].iemask = 0;
- cpu_mask_tbl[4] = IE_IRQ5;
- cpu_irq_nr[4] = FPU;
+ /*
+ * Enable board interrupts: FPU, I/O ASIC.
+ */
+ set_cp0_status(DEC_IE_FPU | dec_ie_ioasic);
- dec_halt_init(&irq10);
+ dec_halt_init(&haltirq);
} /* dec_init_kn03 */
diff -up --recursive --new-file
linux-mips-2.4.14-20011123-dist/arch/mips/kernel/entry.S
linux-mips-2.4.14-20011123-dec-fpu/arch/mips/kernel/entry.S
--- linux-mips-2.4.14-20011123-dist/arch/mips/kernel/entry.S Tue Nov 20
05:26:20 2001
+++ linux-mips-2.4.14-20011123-dec-fpu/arch/mips/kernel/entry.S Fri Nov 23
14:50:11 2001
@@ -106,16 +106,16 @@ LEAF(spurious_interrupt)
__INIT
- /*
- * General exception vector. Used for all CPUs except R4000
- * and R4400 SC and MC versions.
- */
.set reorder
NESTED(except_vec1_generic, 0, sp)
PANIC("Exception vector 1 called")
END(except_vec1_generic)
+ /*
+ * General exception vector. Used for all CPUs except R4000
+ * and R4400 SC and MC versions.
+ */
NESTED(except_vec3_generic, 0, sp)
mfc0 k1, CP0_CAUSE
la k0, exception_handlers
@@ -225,6 +225,7 @@ EXPORT(exception_count_##exception);
NESTED(handle_##exception, PT_SIZE, sp); \
.set noat; \
SAVE_ALL; \
+ FEXPORT(handle_##exception##_int); \
__BUILD_clear_##clear(exception); \
.set at; \
__BUILD_##verbose(exception); \
diff -up --recursive --new-file
linux-mips-2.4.14-20011123-dist/arch/mips/kernel/setup.c
linux-mips-2.4.14-20011123-dec-fpu/arch/mips/kernel/setup.c
--- linux-mips-2.4.14-20011123-dist/arch/mips/kernel/setup.c Tue Nov 20
05:26:20 2001
+++ linux-mips-2.4.14-20011123-dec-fpu/arch/mips/kernel/setup.c Fri Nov 23
14:50:11 2001
@@ -7,7 +7,7 @@
* Copyright (C) 1995 Waldorf Electronics
* Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001 Ralf Baechle
* Copyright (C) 1996 Stoned Elipot
- * Copyright (C) 2000 Maciej W. Rozycki
+ * Copyright (C) 2000, 2001 Maciej W. Rozycki
*/
#include <linux/config.h>
#include <linux/errno.h>
@@ -182,6 +182,28 @@ static inline int cpu_has_confreg(void)
#endif
}
+/*
+ * Get the FPU Implementation/Revision.
+ */
+static inline unsigned long cpu_get_fpu_id(void)
+{
+ unsigned long tmp, fpu_id;
+
+ tmp = read_32bit_cp0_register(CP0_STATUS);
+ write_32bit_cp0_register(CP0_STATUS, tmp | ST0_CU1);
+ fpu_id = read_32bit_cp1_register(CP1_REVISION);
+ write_32bit_cp0_register(CP0_STATUS, tmp);
+ return fpu_id;
+}
+
+/*
+ * Check the CPU has an FPU the official way.
+ */
+static inline int cpu_has_fpu(void)
+{
+ return ((cpu_get_fpu_id() & 0xff00) != FPIR_IMP_NONE);
+}
+
/* declaration of the global struct */
struct mips_cpu mips_cpu = {PRID_IMP_UNKNOWN, CPU_UNKNOWN, 0, 0, 0,
{0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}};
@@ -206,6 +228,8 @@ static inline void cpu_probe(void)
mips_cpu.cputype = CPU_R2000;
mips_cpu.isa_level = MIPS_CPU_ISA_I;
mips_cpu.options = MIPS_CPU_TLB;
+ if (cpu_has_fpu())
+ mips_cpu.options |= MIPS_CPU_FPU;
mips_cpu.tlbsize = 64;
break;
case PRID_IMP_R3000:
@@ -218,6 +242,8 @@ static inline void cpu_probe(void)
mips_cpu.cputype = CPU_R3000;
mips_cpu.isa_level = MIPS_CPU_ISA_I;
mips_cpu.options = MIPS_CPU_TLB;
+ if (cpu_has_fpu())
+ mips_cpu.options |= MIPS_CPU_FPU;
mips_cpu.tlbsize = 64;
break;
case PRID_IMP_R4000:
diff -up --recursive --new-file
linux-mips-2.4.14-20011123-dist/arch/mips/kernel/traps.c
linux-mips-2.4.14-20011123-dec-fpu/arch/mips/kernel/traps.c
--- linux-mips-2.4.14-20011123-dist/arch/mips/kernel/traps.c Wed Nov 21
05:26:46 2001
+++ linux-mips-2.4.14-20011123-dec-fpu/arch/mips/kernel/traps.c Fri Nov 23
14:50:11 2001
@@ -501,7 +501,6 @@ asmlinkage void do_fpe(struct pt_regs *r
return;
force_sig(SIGFPE, current);
- printk(KERN_DEBUG "Sent send SIGFPE to %s\n", current->comm);
}
static inline int get_insn_opcode(struct pt_regs *regs, unsigned int *opcode)
diff -up --recursive --new-file
linux-mips-2.4.14-20011123-dist/include/asm-mips/cpu.h
linux-mips-2.4.14-20011123-dec-fpu/include/asm-mips/cpu.h
--- linux-mips-2.4.14-20011123-dist/include/asm-mips/cpu.h Fri Nov 23
14:46:34 2001
+++ linux-mips-2.4.14-20011123-dec-fpu/include/asm-mips/cpu.h Fri Nov 23
15:08:18 2001
@@ -90,6 +90,17 @@
#define PRID_REV_TX3927C 0x0042
#define PRID_REV_TX39H3TEG 0x0050
+/*
+ * FPU implementation/revision register (CP1 control register 0).
+ *
+ * +---------------------------------+----------------+----------------+
+ * | 0 | Implementation | Revision |
+ * +---------------------------------+----------------+----------------+
+ * 31 16 15 8 7 0
+ */
+
+#define FPIR_IMP_NONE 0x0000
+
#ifndef _LANGUAGE_ASSEMBLY
/*
* Capability and feature descriptor structure for MIPS CPU
diff -up --recursive --new-file
linux-mips-2.4.14-20011123-dist/include/asm-mips/dec/interrupts.h
linux-mips-2.4.14-20011123-dec-fpu/include/asm-mips/dec/interrupts.h
--- linux-mips-2.4.14-20011123-dist/include/asm-mips/dec/interrupts.h Tue Jul
3 04:27:22 2001
+++ linux-mips-2.4.14-20011123-dec-fpu/include/asm-mips/dec/interrupts.h
Wed Nov 21 23:57:52 2001
@@ -13,6 +13,8 @@
#ifndef __ASM_DEC_INTERRUPTS_H
#define __ASM_DEC_INTERRUPTS_H
+#include <asm/mipsregs.h>
+
/*
* DECstation Interrupts
*/
@@ -22,7 +24,7 @@
* Exception: on kmins we have to handle Memory Error
* Interrupts before the TC Interrupts.
*/
-#define CLOCK 0
+#define CLOCK 0
#define SCSI_DMA_INT 1
#define SCSI_INT 2
#define ETHER 3
@@ -31,10 +33,17 @@
#define TC1 6
#define TC2 7
#define MEMORY 8
-#define FPU 9
-#define HALT 10
+#define HALT 9
+
+#define NR_INTS 10
-#define NR_INTS 11
+/*
+ * The FPU is special. It must always be handled first.
+ * Since it bypasses the regular IRQ handler we define
+ * the line it uses here. All DECstations use the same
+ * one.
+ */
+#define DEC_IE_FPU IE_IRQ5
#ifndef __ASSEMBLY__
/*
@@ -70,8 +79,6 @@ extern long asic_mask_tbl[32];
* Common interrupt routine prototypes for all DECStations
*/
extern void dec_intr_unimplemented(void);
-extern void dec_intr_fpu(void);
-extern void dec_intr_rtc(void);
extern void kn02_io_int(void);
extern void kn02xa_io_int(void);
diff -up --recursive --new-file
linux-mips-2.4.14-20011123-dist/include/asm-mips/mipsregs.h
linux-mips-2.4.14-20011123-dec-fpu/include/asm-mips/mipsregs.h
--- linux-mips-2.4.14-20011123-dist/include/asm-mips/mipsregs.h Tue Nov 20
05:27:07 2001
+++ linux-mips-2.4.14-20011123-dec-fpu/include/asm-mips/mipsregs.h Fri Nov
23 14:55:27 2001
@@ -508,6 +508,19 @@
:"=r" (__res)); \
__res;})
+/*
+ * Macros to access the floating point coprocessor control registers
+ */
+#define read_32bit_cp1_register(source) \
+({ int __res; \
+ __asm__ __volatile__( \
+ ".set\tpush\n\t" \
+ ".set\treorder\n\t" \
+ "cfc1\t%0,"STR(source)"\n\t" \
+ ".set\tpop" \
+ : "=r" (__res)); \
+ __res;})
+
/* TLB operations. */
static inline void tlb_probe(void)
{
|