From b4d05cb9cbbf206ab0dc2c1740938b87b3d3ee44 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 29 Mar 2006 14:09:14 +0100 Subject: [MIPS] Make set_vi_srs_handler static. Signed-off-by: Ralf Baechle --- include/asm-mips/system.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h index 4097fac5ac3c..29c55e7ab311 100644 --- a/include/asm-mips/system.h +++ b/include/asm-mips/system.h @@ -440,7 +440,6 @@ static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old, extern void set_handler (unsigned long offset, void *addr, unsigned long len); extern void set_uncached_handler (unsigned long offset, void *addr, unsigned long len); extern void *set_vi_handler (int n, void *addr); -extern void *set_vi_srs_handler (int n, void *addr, int regset); extern void *set_except_vector(int n, void *addr); extern void per_cpu_trap_init(void); -- cgit v1.2.3 From 15c4f67ab81b07d3b579a11fc4a30da84c0d4858 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 29 Mar 2006 18:51:06 +0100 Subject: [MIPS] Provide access functions for c0_badvaddr. Signed-off-by: Ralf Baechle --- include/asm-mips/mipsregs.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/asm-mips/mipsregs.h b/include/asm-mips/mipsregs.h index 035ba0a9b0df..e85a42e2ea0c 100644 --- a/include/asm-mips/mipsregs.h +++ b/include/asm-mips/mipsregs.h @@ -836,6 +836,9 @@ do { \ #define read_c0_cache() __read_32bit_c0_register($7, 0) /* TX39xx */ #define write_c0_cache(val) __write_32bit_c0_register($7, 0, val) +#define read_c0_badvaddr() __read_ulong_c0_register($8, 0) +#define write_c0_badvaddr(val) __write_ulong_c0_register($8, 0, val) + #define read_c0_count() __read_32bit_c0_register($9, 0) #define write_c0_count(val) __write_32bit_c0_register($9, 0, val) -- cgit v1.2.3 From 91b05e6776e173da5ce7c96d67b3ad186c4fa49f Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 29 Mar 2006 18:53:00 +0100 Subject: [MIPS] Fix vectored interrupt support in TLB exception handler generator. Signed-off-by: Ralf Baechle --- arch/mips/mm/tlbex.c | 4 ++-- include/asm-mips/system.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 599b3c297186..c5eea6ae12ca 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -742,7 +742,7 @@ static void __init build_r3000_tlb_refill_handler(void) } #endif - memcpy((void *)CAC_BASE, tlb_handler, 0x80); + memcpy((void *)ebase, tlb_handler, 0x80); } /* @@ -1247,7 +1247,7 @@ static void __init build_r4000_tlb_refill_handler(void) } #endif - memcpy((void *)CAC_BASE, final_handler, 0x100); + memcpy((void *)ebase, final_handler, 0x100); } /* diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h index 29c55e7ab311..39026690d9e4 100644 --- a/include/asm-mips/system.h +++ b/include/asm-mips/system.h @@ -441,6 +441,7 @@ extern void set_handler (unsigned long offset, void *addr, unsigned long len); extern void set_uncached_handler (unsigned long offset, void *addr, unsigned long len); extern void *set_vi_handler (int n, void *addr); extern void *set_except_vector(int n, void *addr); +extern unsigned long ebase; extern void per_cpu_trap_init(void); extern NORET_TYPE void die(const char *, struct pt_regs *); -- cgit v1.2.3 From 84ada9f85686a6bfdbd65c24fd23ef5d641b0776 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 30 Mar 2006 21:27:47 +0100 Subject: [MIPS] More SHT_* and SHF_* ELF definitions. Signed-off-by: Ralf Baechle --- include/asm-mips/elf.h | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-mips/elf.h b/include/asm-mips/elf.h index 851f013adad3..bdc9de2df1ef 100644 --- a/include/asm-mips/elf.h +++ b/include/asm-mips/elf.h @@ -119,8 +119,49 @@ #define SHT_MIPS_CONFLICT 0x70000002 #define SHT_MIPS_GPTAB 0x70000003 #define SHT_MIPS_UCODE 0x70000004 - -#define SHF_MIPS_GPREL 0x10000000 +#define SHT_MIPS_DEBUG 0x70000005 +#define SHT_MIPS_REGINFO 0x70000006 +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + +#define SHF_MIPS_GPREL 0x10000000 +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRING 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPES 0x01000000 #ifndef ELF_ARCH /* ELF register definitions */ -- cgit v1.2.3 From f115da9cd60ccd5f27941dcf9fe8038ae9486a77 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Fri, 31 Mar 2006 09:27:20 +0100 Subject: [MIPS] Wire splice syscall. Signed-off-by: Ralf Baechle --- arch/mips/kernel/scall32-o32.S | 1 + arch/mips/kernel/scall64-64.S | 1 + arch/mips/kernel/scall64-n32.S | 1 + arch/mips/kernel/scall64-o32.S | 1 + include/asm-mips/unistd.h | 15 +++++++++------ 5 files changed, 13 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S index 2f2dc54b2e26..c05ba056b1e4 100644 --- a/arch/mips/kernel/scall32-o32.S +++ b/arch/mips/kernel/scall32-o32.S @@ -634,6 +634,7 @@ einval: li v0, -EINVAL sys sys_pselect6 6 sys sys_ppoll 5 sys sys_unshare 1 + sys sys_splice 4 .endm /* We pre-compute the number of _instruction_ bytes needed to diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S index 98bf25df56f3..f7c4e751f54a 100644 --- a/arch/mips/kernel/scall64-64.S +++ b/arch/mips/kernel/scall64-64.S @@ -460,3 +460,4 @@ sys_call_table: PTR sys_pselect6 /* 5260 */ PTR sys_ppoll PTR sys_unshare + PTR sys_splice diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index 05a2c0567dae..52aea55d9c5a 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -386,3 +386,4 @@ EXPORT(sysn32_call_table) PTR sys_pselect6 PTR sys_ppoll /* 6265 */ PTR sys_unshare + PTR sys_splice diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index 19c4ca481b02..c7e766e46368 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -508,4 +508,5 @@ sys_call_table: PTR sys_pselect6 PTR sys_ppoll PTR sys_unshare + PTR sys_splice .size sys_call_table,.-sys_call_table diff --git a/include/asm-mips/unistd.h b/include/asm-mips/unistd.h index b5c78a4a0192..2998795fa25e 100644 --- a/include/asm-mips/unistd.h +++ b/include/asm-mips/unistd.h @@ -324,16 +324,17 @@ #define __NR_pselect6 (__NR_Linux + 301) #define __NR_ppoll (__NR_Linux + 302) #define __NR_unshare (__NR_Linux + 303) +#define __NR_splice (__NR_Linux + 304) /* * Offset of the last Linux o32 flavoured syscall */ -#define __NR_Linux_syscalls 303 +#define __NR_Linux_syscalls 304 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ #define __NR_O32_Linux 4000 -#define __NR_O32_Linux_syscalls 303 +#define __NR_O32_Linux_syscalls 304 #if _MIPS_SIM == _MIPS_SIM_ABI64 @@ -604,16 +605,17 @@ #define __NR_pselect6 (__NR_Linux + 260) #define __NR_ppoll (__NR_Linux + 261) #define __NR_unshare (__NR_Linux + 262) +#define __NR_splice (__NR_Linux + 263) /* * Offset of the last Linux 64-bit flavoured syscall */ -#define __NR_Linux_syscalls 262 +#define __NR_Linux_syscalls 263 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ #define __NR_64_Linux 5000 -#define __NR_64_Linux_syscalls 262 +#define __NR_64_Linux_syscalls 263 #if _MIPS_SIM == _MIPS_SIM_NABI32 @@ -888,16 +890,17 @@ #define __NR_pselect6 (__NR_Linux + 264) #define __NR_ppoll (__NR_Linux + 265) #define __NR_unshare (__NR_Linux + 266) +#define __NR_splice (__NR_Linux + 267) /* * Offset of the last N32 flavoured syscall */ -#define __NR_Linux_syscalls 266 +#define __NR_Linux_syscalls 267 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ #define __NR_N32_Linux 6000 -#define __NR_N32_Linux_syscalls 266 +#define __NR_N32_Linux_syscalls 267 #ifndef __ASSEMBLY__ -- cgit v1.2.3 From a8d587a71b76328447de165b12495650721b9286 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sat, 1 Apr 2006 07:49:21 +0100 Subject: [MIPS] Wire up sync_file_range(2). Signed-off-by: Ralf Baechle --- arch/mips/kernel/linux32.c | 10 ++++++++++ arch/mips/kernel/scall32-o32.S | 1 + arch/mips/kernel/scall64-64.S | 1 + arch/mips/kernel/scall64-n32.S | 1 + arch/mips/kernel/scall64-o32.S | 1 + include/asm-mips/unistd.h | 15 +++++++++------ 6 files changed, 23 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index 3f40c37a9ee6..7c953bcc5f6a 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -1182,6 +1182,16 @@ asmlinkage ssize_t sys32_readahead(int fd, u32 pad0, u64 a2, u64 a3, return sys_readahead(fd, merge_64(a2, a3), count); } +asmlinkage long sys32_sync_file_range(int fd, int __pad, + unsigned long a2, unsigned long a3, + unsigned long a4, unsigned long a5, + int flags) +{ + return sys_sync_file_range(fd, + merge_64(a2, a3), merge_64(a4, a5), + flags); +} + /* Argument list sizes for sys_socketcall */ #define AL(x) ((x) * sizeof(unsigned int)) static unsigned char socketcall_nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S index c05ba056b1e4..4e36b87be1ed 100644 --- a/arch/mips/kernel/scall32-o32.S +++ b/arch/mips/kernel/scall32-o32.S @@ -635,6 +635,7 @@ einval: li v0, -EINVAL sys sys_ppoll 5 sys sys_unshare 1 sys sys_splice 4 + sys sys_sync_file_range 7 /* 4305 */ .endm /* We pre-compute the number of _instruction_ bytes needed to diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S index f7c4e751f54a..9ba750887377 100644 --- a/arch/mips/kernel/scall64-64.S +++ b/arch/mips/kernel/scall64-64.S @@ -461,3 +461,4 @@ sys_call_table: PTR sys_ppoll PTR sys_unshare PTR sys_splice + PTR sys_sync_file_range diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index 52aea55d9c5a..942aca26f9c4 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -387,3 +387,4 @@ EXPORT(sysn32_call_table) PTR sys_ppoll /* 6265 */ PTR sys_unshare PTR sys_splice + PTR sys_sync_file_range diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index c7e766e46368..b53a9207f530 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -509,4 +509,5 @@ sys_call_table: PTR sys_ppoll PTR sys_unshare PTR sys_splice + PTR sys32_sync_file_range /* 4305 */ .size sys_call_table,.-sys_call_table diff --git a/include/asm-mips/unistd.h b/include/asm-mips/unistd.h index 2998795fa25e..1068fe9a0a58 100644 --- a/include/asm-mips/unistd.h +++ b/include/asm-mips/unistd.h @@ -325,16 +325,17 @@ #define __NR_ppoll (__NR_Linux + 302) #define __NR_unshare (__NR_Linux + 303) #define __NR_splice (__NR_Linux + 304) +#define __NR_sync_file_range (__NR_Linux + 305) /* * Offset of the last Linux o32 flavoured syscall */ -#define __NR_Linux_syscalls 304 +#define __NR_Linux_syscalls 305 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ #define __NR_O32_Linux 4000 -#define __NR_O32_Linux_syscalls 304 +#define __NR_O32_Linux_syscalls 305 #if _MIPS_SIM == _MIPS_SIM_ABI64 @@ -606,16 +607,17 @@ #define __NR_ppoll (__NR_Linux + 261) #define __NR_unshare (__NR_Linux + 262) #define __NR_splice (__NR_Linux + 263) +#define __NR_sync_file_range (__NR_Linux + 264) /* * Offset of the last Linux 64-bit flavoured syscall */ -#define __NR_Linux_syscalls 263 +#define __NR_Linux_syscalls 264 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ #define __NR_64_Linux 5000 -#define __NR_64_Linux_syscalls 263 +#define __NR_64_Linux_syscalls 264 #if _MIPS_SIM == _MIPS_SIM_NABI32 @@ -891,16 +893,17 @@ #define __NR_ppoll (__NR_Linux + 265) #define __NR_unshare (__NR_Linux + 266) #define __NR_splice (__NR_Linux + 267) +#define __NR_sync_file_range (__NR_Linux + 268) /* * Offset of the last N32 flavoured syscall */ -#define __NR_Linux_syscalls 267 +#define __NR_Linux_syscalls 268 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ #define __NR_N32_Linux 6000 -#define __NR_N32_Linux_syscalls 267 +#define __NR_N32_Linux_syscalls 268 #ifndef __ASSEMBLY__ -- cgit v1.2.3 From 93373ed4d87fb02554ce020d929388ac16913664 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sat, 1 Apr 2006 21:17:45 +0100 Subject: [MIPS] Rewrite spurious_interrupt from assembler to C. Signed-off-by: Ralf Baechle --- arch/mips/au1000/common/int-handler.S | 3 ++- arch/mips/ddb5xxx/ddb5476/int-handler.S | 3 ++- arch/mips/ddb5xxx/ddb5476/vrc5476_irq.c | 4 +--- arch/mips/ddb5xxx/ddb5477/int-handler.S | 4 ++-- arch/mips/dec/int-handler.S | 4 +++- arch/mips/galileo-boards/ev96100/int-handler.S | 3 ++- arch/mips/gt64120/ev64120/int-handler.S | 3 ++- arch/mips/jazz/int-handler.S | 3 ++- arch/mips/kernel/entry.S | 26 -------------------------- arch/mips/kernel/irq.c | 5 +++++ arch/mips/mips-boards/generic/mipsIRQ.S | 4 +++- arch/mips/mips-boards/sim/sim_irq.S | 4 +++- arch/mips/momentum/ocelot_3/int-handler.S | 4 +++- arch/mips/momentum/ocelot_c/int-handler.S | 3 ++- arch/mips/philips/pnx8550/common/mipsIRQ.S | 3 ++- arch/mips/tx4927/common/tx4927_irq_handler.S | 3 ++- arch/mips/vr41xx/common/int-handler.S | 4 +++- include/asm-mips/irq.h | 1 + 18 files changed, 40 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/arch/mips/au1000/common/int-handler.S b/arch/mips/au1000/common/int-handler.S index 1c4ca883321e..65baa8a8c522 100644 --- a/arch/mips/au1000/common/int-handler.S +++ b/arch/mips/au1000/common/int-handler.S @@ -64,5 +64,6 @@ NESTED(au1000_IRQ, PT_SIZE, sp) 5: move a0, sp - j spurious_interrupt + jal spurious_interrupt + j ret_from_irq END(au1000_IRQ) diff --git a/arch/mips/ddb5xxx/ddb5476/int-handler.S b/arch/mips/ddb5xxx/ddb5476/int-handler.S index 12c292e189ba..0c2bdae96bb1 100644 --- a/arch/mips/ddb5xxx/ddb5476/int-handler.S +++ b/arch/mips/ddb5xxx/ddb5476/int-handler.S @@ -54,7 +54,8 @@ .set reorder /* wrong alarm or masked ... */ - // j spurious_interrupt + // jal spurious_interrupt + // j ret_from_irq move a0, sp jal vrc5476_irq_dispatch j ret_from_irq diff --git a/arch/mips/ddb5xxx/ddb5476/vrc5476_irq.c b/arch/mips/ddb5xxx/ddb5476/vrc5476_irq.c index f66fe5b58636..581eabad5f82 100644 --- a/arch/mips/ddb5xxx/ddb5476/vrc5476_irq.c +++ b/arch/mips/ddb5xxx/ddb5476/vrc5476_irq.c @@ -80,8 +80,6 @@ vrc5476_irq_init(u32 base) asmlinkage void vrc5476_irq_dispatch(struct pt_regs *regs) { - extern void spurious_interrupt(void); - u32 mask; int nile4_irq; @@ -107,5 +105,5 @@ vrc5476_irq_dispatch(struct pt_regs *regs) return; } } - spurious_interrupt(); + spurious_interrupt(regs); } diff --git a/arch/mips/ddb5xxx/ddb5477/int-handler.S b/arch/mips/ddb5xxx/ddb5477/int-handler.S index a2502a14400e..9884874dbeb5 100644 --- a/arch/mips/ddb5xxx/ddb5477/int-handler.S +++ b/arch/mips/ddb5xxx/ddb5477/int-handler.S @@ -44,8 +44,8 @@ .set reorder /* wrong alarm or masked ... */ - j spurious_interrupt - nop + jal spurious_interrupt + j ret_from_irq END(ddb5477_handle_int) .align 5 diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S index 41fa372007bf..5bafd585ac3e 100644 --- a/arch/mips/dec/int-handler.S +++ b/arch/mips/dec/int-handler.S @@ -282,7 +282,9 @@ fpu: #endif spurious: - j spurious_interrupt + jal spurious_interrupt + nop + j ret_from_irq nop END(decstation_handle_int) diff --git a/arch/mips/galileo-boards/ev96100/int-handler.S b/arch/mips/galileo-boards/ev96100/int-handler.S index ff4d10a38859..0edf1fec2905 100644 --- a/arch/mips/galileo-boards/ev96100/int-handler.S +++ b/arch/mips/galileo-boards/ev96100/int-handler.S @@ -29,5 +29,6 @@ NESTED(ev96100IRQ, PT_SIZE, sp) jal ev96100_cpu_irq j ret_from_irq -3: j spurious_interrupt +3: jal spurious_interrupt + j ret_from_irq END(ev96100IRQ) diff --git a/arch/mips/gt64120/ev64120/int-handler.S b/arch/mips/gt64120/ev64120/int-handler.S index 752435faf2de..9dda5b449522 100644 --- a/arch/mips/gt64120/ev64120/int-handler.S +++ b/arch/mips/gt64120/ev64120/int-handler.S @@ -39,8 +39,9 @@ nop /* wrong alarm or masked ... */ - j spurious_interrupt + jal spurious_interrupt nop + j ret_from_irq END(galileo_handle_int) diff --git a/arch/mips/jazz/int-handler.S b/arch/mips/jazz/int-handler.S index dc752c67b528..e35f5fcd3f90 100644 --- a/arch/mips/jazz/int-handler.S +++ b/arch/mips/jazz/int-handler.S @@ -263,7 +263,8 @@ loc_call: /* /* * "Jump extender" to reach spurious_interrupt */ -3: j spurious_interrupt +3: jal spurious_interrupt + j ret_from_irq /* * Vectors for interrupts generated by local devices diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S index 83c87fe4ee4f..371571f4f280 100644 --- a/arch/mips/kernel/entry.S +++ b/arch/mips/kernel/entry.S @@ -119,29 +119,3 @@ syscall_exit_work: li a1, 1 jal do_syscall_trace b resume_userspace - -/* - * Common spurious interrupt handler. - */ -LEAF(spurious_interrupt) - /* - * Someone tried to fool us by sending an interrupt but we - * couldn't find a cause for it. - */ - PTR_LA t1, irq_err_count -#ifdef CONFIG_SMP -1: ll t0, (t1) - addiu t0, 1 - sc t0, (t1) -#if R10000_LLSC_WAR - beqzl t0, 1b -#else - beqz t0, 1b -#endif -#else - lw t0, (t1) - addiu t0, 1 - sw t0, (t1) -#endif - j ret_from_irq - END(spurious_interrupt) diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index 3dd76b3d2967..e0efc4f2f93e 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -101,6 +101,11 @@ skip: return 0; } +asmlinkage void spurious_interrupt(struct pt_regs *regs) +{ + atomic_inc(&irq_err_count); +} + #ifdef CONFIG_KGDB extern void breakpoint(void); extern void set_debug_traps(void); diff --git a/arch/mips/mips-boards/generic/mipsIRQ.S b/arch/mips/mips-boards/generic/mipsIRQ.S index ddd5c73a2971..973e10aaacd5 100644 --- a/arch/mips/mips-boards/generic/mipsIRQ.S +++ b/arch/mips/mips-boards/generic/mipsIRQ.S @@ -150,6 +150,8 @@ spurious: - j spurious_interrupt + jal spurious_interrupt + nop + j ret_from_irq nop END(mipsIRQ) diff --git a/arch/mips/mips-boards/sim/sim_irq.S b/arch/mips/mips-boards/sim/sim_irq.S index da52297a2216..d16cf3822076 100644 --- a/arch/mips/mips-boards/sim/sim_irq.S +++ b/arch/mips/mips-boards/sim/sim_irq.S @@ -94,6 +94,8 @@ spurious: - j spurious_interrupt + jal spurious_interrupt + nop + j ret_from_irq nop END(simIRQ) diff --git a/arch/mips/momentum/ocelot_3/int-handler.S b/arch/mips/momentum/ocelot_3/int-handler.S index 4522f09ed769..b1207262984a 100644 --- a/arch/mips/momentum/ocelot_3/int-handler.S +++ b/arch/mips/momentum/ocelot_3/int-handler.S @@ -78,8 +78,10 @@ .set reorder /* wrong alarm or masked ... */ - j spurious_interrupt + jal spurious_interrupt nop + j ret_from_irq + nop END(ocelot3_handle_int) .align 5 diff --git a/arch/mips/momentum/ocelot_c/int-handler.S b/arch/mips/momentum/ocelot_c/int-handler.S index 52349d9bf1be..f77834193c3c 100644 --- a/arch/mips/momentum/ocelot_c/int-handler.S +++ b/arch/mips/momentum/ocelot_c/int-handler.S @@ -52,8 +52,9 @@ .set reorder /* wrong alarm or masked ... */ - j spurious_interrupt + jal spurious_interrupt nop + j ret_from_irq END(ocelot_handle_int) .align 5 diff --git a/arch/mips/philips/pnx8550/common/mipsIRQ.S b/arch/mips/philips/pnx8550/common/mipsIRQ.S index 338bffda3fab..e049a719f83d 100644 --- a/arch/mips/philips/pnx8550/common/mipsIRQ.S +++ b/arch/mips/philips/pnx8550/common/mipsIRQ.S @@ -46,8 +46,9 @@ /* wrong alarm or masked ... */ - j spurious_interrupt + jal spurious_interrupt nop + j ret_from_irq END(cp0_irqdispatch) .align 5 diff --git a/arch/mips/tx4927/common/tx4927_irq_handler.S b/arch/mips/tx4927/common/tx4927_irq_handler.S index dd3ceda9d712..0b2ea02574f2 100644 --- a/arch/mips/tx4927/common/tx4927_irq_handler.S +++ b/arch/mips/tx4927/common/tx4927_irq_handler.S @@ -63,8 +63,9 @@ .set reorder /* wrong alarm or masked ... */ - j spurious_interrupt + jal spurious_interrupt nop + j ret_from_irq END(tx4927_irq_handler) .align 5 diff --git a/arch/mips/vr41xx/common/int-handler.S b/arch/mips/vr41xx/common/int-handler.S index 2b6043f16d09..e8652348fef1 100644 --- a/arch/mips/vr41xx/common/int-handler.S +++ b/arch/mips/vr41xx/common/int-handler.S @@ -98,8 +98,10 @@ bnez t1, handle_irq li a0, 1 - j spurious_interrupt + jal spurious_interrupt nop + j ret_from_irq + nop handle_int: jal irq_dispatch diff --git a/include/asm-mips/irq.h b/include/asm-mips/irq.h index 8a342ccb34a8..d7aecca3b95f 100644 --- a/include/asm-mips/irq.h +++ b/include/asm-mips/irq.h @@ -46,5 +46,6 @@ do { \ #endif extern void arch_init_irq(void); +extern void spurious_interrupt(struct pt_regs *regs); #endif /* _ASM_IRQ_H */ -- cgit v1.2.3 From 13626a887fad4220bc7ca85f4b42ca8cfb805e11 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sun, 2 Apr 2006 13:17:58 +0100 Subject: [MIPS] MV6434x: The name of the CPP symbol is __mips__, not __MIPS__. Signed-off-by: Ralf Baechle --- include/linux/mv643xx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mv643xx.h b/include/linux/mv643xx.h index 955d3069d727..edfa012fad3a 100644 --- a/include/linux/mv643xx.h +++ b/include/linux/mv643xx.h @@ -13,7 +13,7 @@ #ifndef __ASM_MV643XX_H #define __ASM_MV643XX_H -#ifdef __MIPS__ +#ifdef __mips__ #include #include #endif -- cgit v1.2.3 From ac2384a855c94e1d5467afe20bcb8ca23f0f3853 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sun, 2 Apr 2006 13:48:57 +0100 Subject: [MIPS] it8172: Fix build of serial driver. Signed-off-by: Ralf Baechle --- include/asm-mips/serial.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/asm-mips/serial.h b/include/asm-mips/serial.h index 7b2366412203..7196ceb0e948 100644 --- a/include/asm-mips/serial.h +++ b/include/asm-mips/serial.h @@ -77,15 +77,15 @@ #include #define ITE_SERIAL_PORT_DEFNS \ { .baud_base = BASE_BAUD, .port = (IT8172_PCI_IO_BASE + IT_UART_BASE), \ - .irq = IT8172_UART_IRQ, .flags = STD_COM_FLAGS, .type = 0x3 }, \ + .irq = IT8172_UART_IRQ, .flags = STD_COM_FLAGS, .port = PORT_16550 }, \ { .baud_base = (24000000/(16*13)), .port = (IT8172_PCI_IO_BASE + IT8712_UART1_PORT), \ - .irq = IT8172_SERIRQ_4, .flags = STD_COM_FLAGS, .type = 0x3 }, \ + .irq = IT8172_SERIRQ_4, .flags = STD_COM_FLAGS, .port = PORT_16550 }, \ /* Smart Card Reader 0 */ \ { .baud_base = BASE_BAUD, .port = (IT8172_PCI_IO_BASE + IT_SCR0_BASE), \ - .irq = IT8172_SCR0_IRQ, .flags = STD_COM_FLAGS, .type = 0x3 }, \ + .irq = IT8172_SCR0_IRQ, .flags = STD_COM_FLAGS, .port = PORT_16550 }, \ /* Smart Card Reader 1 */ \ { .baud_base = BASE_BAUD, .port = (IT8172_PCI_IO_BASE + IT_SCR1_BASE), \ - .irq = IT8172_SCR1_IRQ, .flags = STD_COM_FLAGS, .type = 0x3 }, + .irq = IT8172_SCR1_IRQ, .flags = STD_COM_FLAGS, .port = PORT_16550 }, #else #define ITE_SERIAL_PORT_DEFNS #endif @@ -95,10 +95,10 @@ #include #define IVR_SERIAL_PORT_DEFNS \ { .baud_base = BASE_BAUD, .port = (IT8172_PCI_IO_BASE + IT_UART_BASE), \ - .irq = IT8172_UART_IRQ, .flags = STD_COM_FLAGS, .type = 0x3 }, \ + .irq = IT8172_UART_IRQ, .flags = STD_COM_FLAGS, .port = PORT_16550 }, \ /* Smart Card Reader 1 */ \ { .baud_base = BASE_BAUD, .port = (IT8172_PCI_IO_BASE + IT_SCR1_BASE), \ - .irq = IT8172_SCR1_IRQ, .flags = STD_COM_FLAGS, .type = 0x3 }, + .irq = IT8172_SCR1_IRQ, .flags = STD_COM_FLAGS, .port = PORT_16550 }, #else #define IVR_SERIAL_PORT_DEFNS #endif -- cgit v1.2.3 From 088cf96a692a0369973aa19dcbf36134d9e6a529 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sun, 2 Apr 2006 18:06:43 +0100 Subject: [MIPS] MV6434x: Add prototype of interrupt dispatch function. Signed-off-by: Ralf Baechle --- include/asm-mips/marvell.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/asm-mips/marvell.h b/include/asm-mips/marvell.h index 9225b3397a4f..6bb2125bb053 100644 --- a/include/asm-mips/marvell.h +++ b/include/asm-mips/marvell.h @@ -53,4 +53,6 @@ struct mv_pci_controller { unsigned long config_vreg; }; +extern void ll_mv64340_irq(struct pt_regs *regs); + #endif /* __ASM_MIPS_MARVELL_H */ -- cgit v1.2.3 From ba8990f2aec85b5b62643aa82a1e71c738efc487 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Mon, 3 Apr 2006 00:21:30 +0100 Subject: [MIPS] JMR3927 build fixes for the RTC code. Signed-off-by: Ralf Baechle --- arch/mips/jmr3927/common/rtc_ds1742.c | 60 +++++++++++++++++----------------- include/asm-mips/ds1742.h | 13 ++++++++ include/asm-mips/mach-jmr3927/ds1742.h | 4 +-- 3 files changed, 45 insertions(+), 32 deletions(-) create mode 100644 include/asm-mips/ds1742.h (limited to 'include') diff --git a/arch/mips/jmr3927/common/rtc_ds1742.c b/arch/mips/jmr3927/common/rtc_ds1742.c index a6bd3f4d3049..e6561345d12a 100644 --- a/arch/mips/jmr3927/common/rtc_ds1742.c +++ b/arch/mips/jmr3927/common/rtc_ds1742.c @@ -60,15 +60,15 @@ rtc_ds1742_get_time(void) unsigned long flags; spin_lock_irqsave(&rtc_lock, flags); - CMOS_WRITE(RTC_READ, RTC_CONTROL); - second = BCD2BIN(CMOS_READ(RTC_SECONDS) & RTC_SECONDS_MASK); - minute = BCD2BIN(CMOS_READ(RTC_MINUTES)); - hour = BCD2BIN(CMOS_READ(RTC_HOURS)); - day = BCD2BIN(CMOS_READ(RTC_DATE)); - month = BCD2BIN(CMOS_READ(RTC_MONTH)); - year = BCD2BIN(CMOS_READ(RTC_YEAR)); - century = BCD2BIN(CMOS_READ(RTC_CENTURY) & RTC_CENTURY_MASK); - CMOS_WRITE(0, RTC_CONTROL); + rtc_write(RTC_READ, RTC_CONTROL); + second = BCD2BIN(rtc_read(RTC_SECONDS) & RTC_SECONDS_MASK); + minute = BCD2BIN(rtc_read(RTC_MINUTES)); + hour = BCD2BIN(rtc_read(RTC_HOURS)); + day = BCD2BIN(rtc_read(RTC_DATE)); + month = BCD2BIN(rtc_read(RTC_MONTH)); + year = BCD2BIN(rtc_read(RTC_YEAR)); + century = BCD2BIN(rtc_read(RTC_CENTURY) & RTC_CENTURY_MASK); + rtc_write(0, RTC_CONTROL); spin_unlock_irqrestore(&rtc_lock, flags); year += century * 100; @@ -87,16 +87,16 @@ rtc_ds1742_set_time(unsigned long t) unsigned long flags; spin_lock_irqsave(&rtc_lock, flags); - CMOS_WRITE(RTC_READ, RTC_CONTROL); - cmos_second = (u8)(CMOS_READ(RTC_SECONDS) & RTC_SECONDS_MASK); - cmos_minute = (u8)CMOS_READ(RTC_MINUTES); - cmos_hour = (u8)CMOS_READ(RTC_HOURS); - cmos_day = (u8)CMOS_READ(RTC_DATE); - cmos_month = (u8)CMOS_READ(RTC_MONTH); - cmos_year = (u8)CMOS_READ(RTC_YEAR); - cmos_century = CMOS_READ(RTC_CENTURY) & RTC_CENTURY_MASK; + rtc_write(RTC_READ, RTC_CONTROL); + cmos_second = (u8)(rtc_read(RTC_SECONDS) & RTC_SECONDS_MASK); + cmos_minute = (u8)rtc_read(RTC_MINUTES); + cmos_hour = (u8)rtc_read(RTC_HOURS); + cmos_day = (u8)rtc_read(RTC_DATE); + cmos_month = (u8)rtc_read(RTC_MONTH); + cmos_year = (u8)rtc_read(RTC_YEAR); + cmos_century = rtc_read(RTC_CENTURY) & RTC_CENTURY_MASK; - CMOS_WRITE(RTC_WRITE, RTC_CONTROL); + rtc_write(RTC_WRITE, RTC_CONTROL); /* convert */ to_tm(t, &tm); @@ -104,18 +104,18 @@ rtc_ds1742_set_time(unsigned long t) /* check each field one by one */ year = BIN2BCD(tm.tm_year - EPOCH); if (year != cmos_year) { - CMOS_WRITE(year,RTC_YEAR); + rtc_write(year,RTC_YEAR); } month = BIN2BCD(tm.tm_mon); if (month != (cmos_month & 0x1f)) { - CMOS_WRITE((month & 0x1f) | (cmos_month & ~0x1f),RTC_MONTH); + rtc_write((month & 0x1f) | (cmos_month & ~0x1f),RTC_MONTH); } day = BIN2BCD(tm.tm_mday); if (day != cmos_day) { - CMOS_WRITE(day, RTC_DATE); + rtc_write(day, RTC_DATE); } if (cmos_hour & 0x40) { @@ -130,20 +130,20 @@ rtc_ds1742_set_time(unsigned long t) /* 24 hour format */ hour = BIN2BCD(tm.tm_hour) & 0x3f; } - if (hour != cmos_hour) CMOS_WRITE(hour, RTC_HOURS); + if (hour != cmos_hour) rtc_write(hour, RTC_HOURS); minute = BIN2BCD(tm.tm_min); if (minute != cmos_minute) { - CMOS_WRITE(minute, RTC_MINUTES); + rtc_write(minute, RTC_MINUTES); } second = BIN2BCD(tm.tm_sec); if (second != cmos_second) { - CMOS_WRITE(second & RTC_SECONDS_MASK,RTC_SECONDS); + rtc_write(second & RTC_SECONDS_MASK,RTC_SECONDS); } /* RTC_CENTURY and RTC_CONTROL share same address... */ - CMOS_WRITE(cmos_century, RTC_CONTROL); + rtc_write(cmos_century, RTC_CONTROL); spin_unlock_irqrestore(&rtc_lock, flags); return 0; @@ -163,9 +163,9 @@ rtc_ds1742_init(unsigned long base) rtc_mips_set_time = rtc_ds1742_set_time; /* clear oscillator stop bit */ - CMOS_WRITE(RTC_READ, RTC_CONTROL); - cmos_second = (u8)(CMOS_READ(RTC_SECONDS) & RTC_SECONDS_MASK); - CMOS_WRITE(RTC_WRITE, RTC_CONTROL); - CMOS_WRITE(cmos_second, RTC_SECONDS); /* clear msb */ - CMOS_WRITE(0, RTC_CONTROL); + rtc_write(RTC_READ, RTC_CONTROL); + cmos_second = (u8)(rtc_read(RTC_SECONDS) & RTC_SECONDS_MASK); + rtc_write(RTC_WRITE, RTC_CONTROL); + rtc_write(cmos_second, RTC_SECONDS); /* clear msb */ + rtc_write(0, RTC_CONTROL); } diff --git a/include/asm-mips/ds1742.h b/include/asm-mips/ds1742.h new file mode 100644 index 000000000000..c2f2c32da637 --- /dev/null +++ b/include/asm-mips/ds1742.h @@ -0,0 +1,13 @@ +/* + * 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) 2006 by Ralf Baechle (ralf@linux-mips.org) + */ +#ifndef _ASM_DS1742_H +#define _ASM_DS1742_H + +#include + +#endif /* _ASM_DS1742_H */ diff --git a/include/asm-mips/mach-jmr3927/ds1742.h b/include/asm-mips/mach-jmr3927/ds1742.h index cff6192d4bdb..8a8fef6d07fa 100644 --- a/include/asm-mips/mach-jmr3927/ds1742.h +++ b/include/asm-mips/mach-jmr3927/ds1742.h @@ -3,14 +3,14 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2003 by Ralf Baechle + * Copyright (C) 2003, 06 by Ralf Baechle */ #ifndef __ASM_MACH_JMR3927_DS1742_H #define __ASM_MACH_JMR3927_DS1742_H #include -#define rtc_read(reg) (jmr3927_nvram_in(addr)) +#define rtc_read(reg) (jmr3927_nvram_in(reg)) #define rtc_write(data, reg) (jmr3927_nvram_out((data),(reg))) #endif /* __ASM_MACH_JMR3927_DS1742_H */ -- cgit v1.2.3 From d35d473c25d43d7db3e5e18b66d558d2a631cca8 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Mon, 3 Apr 2006 13:17:41 +0100 Subject: [MIPS] Fix the crime against humanity that mipsIRQ.S is. Signed-off-by: Ralf Baechle --- arch/mips/mips-boards/atlas/Makefile | 2 +- arch/mips/mips-boards/atlas/atlas-irq.S | 120 ++++++++++++++++++++++++ arch/mips/mips-boards/generic/Makefile | 4 +- arch/mips/mips-boards/generic/mipsIRQ.S | 157 -------------------------------- arch/mips/mips-boards/malta/Makefile | 2 +- arch/mips/mips-boards/malta/malta-irq.S | 122 +++++++++++++++++++++++++ arch/mips/mips-boards/sead/Makefile | 2 +- arch/mips/mips-boards/sead/sead-irq.S | 111 ++++++++++++++++++++++ include/asm-mips/mips-boards/atlas.h | 18 +++- include/asm-mips/mips-boards/atlasint.h | 19 ---- 10 files changed, 375 insertions(+), 182 deletions(-) create mode 100644 arch/mips/mips-boards/atlas/atlas-irq.S delete mode 100644 arch/mips/mips-boards/generic/mipsIRQ.S create mode 100644 arch/mips/mips-boards/malta/malta-irq.S create mode 100644 arch/mips/mips-boards/sead/sead-irq.S (limited to 'include') diff --git a/arch/mips/mips-boards/atlas/Makefile b/arch/mips/mips-boards/atlas/Makefile index d8dab75906bf..50fec2a5aee6 100644 --- a/arch/mips/mips-boards/atlas/Makefile +++ b/arch/mips/mips-boards/atlas/Makefile @@ -16,5 +16,5 @@ # 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # -obj-y := atlas_int.o atlas_setup.o +obj-y := atlas_int.o atlas-irq.o atlas_setup.o obj-$(CONFIG_KGDB) += atlas_gdb.o diff --git a/arch/mips/mips-boards/atlas/atlas-irq.S b/arch/mips/mips-boards/atlas/atlas-irq.S new file mode 100644 index 000000000000..31bc99a52383 --- /dev/null +++ b/arch/mips/mips-boards/atlas/atlas-irq.S @@ -0,0 +1,120 @@ +/* + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999, 2000 MIPS Technologies, Inc. All rights reserved. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Interrupt exception dispatch code. + */ +#include + +#include +#include +#include +#include +#include + +/* + * Furthermore, the IRQs on the MIPS board look basically (barring software + * IRQs which we don't use at all and all external interrupt sources are + * combined together on hardware interrupt 0 (MIPS IRQ 2)) like: + * + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 Combined hardware interrupt (hw0) + * 3 Hardware (ignored) + * 4 Hardware (ignored) + * 5 Hardware (ignored) + * 6 Hardware (ignored) + * 7 R4k timer (what we use) + * + * Note: On the SEAD board thing are a little bit different. + * Here IRQ 2 (hw0) is wired to the UART0 and IRQ 3 (hw1) is wired + * wired to UART1. + * + * We handle the IRQ according to _our_ priority which is: + * + * Highest ---- R4k Timer + * Lowest ---- Combined hardware interrupt + * + * then we just return, if multiple IRQs are pending then we will just take + * another exception, big deal. + */ + + .text + .set noreorder + .set noat + .align 5 + NESTED(mipsIRQ, PT_SIZE, sp) + SAVE_ALL + CLI + .set at + + mfc0 s0, CP0_CAUSE # get irq bits + mfc0 s1, CP0_STATUS # get irq mask + andi s0, ST0_IM # CAUSE.CE may be non-zero! + and s0, s1 + +#if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) + .set mips32 + clz a0, s0 + .set mips0 + negu a0 + addu a0, 31-CAUSEB_IP + bltz a0, spurious +#else + beqz s0, spurious + li a0, 7 + + and t0, s0, 0xf000 + sltiu t0, t0, 1 + sll t0, 2 + subu a0, t0 + sll s0, t0 + + and t0, s0, 0xc000 + sltiu t0, t0, 1 + sll t0, 1 + subu a0, t0 + sll s0, t0 + + and t0, s0, 0x8000 + sltiu t0, t0, 1 + # sll t0, 0 + subu a0, t0 + # sll s0, t0 +#endif + + li a1, MIPSCPU_INT_ATLAS + bne a0, a1, 1f + addu a0, MIPSCPU_INT_BASE + + jal atlas_hw0_irqdispatch + move a0, sp + + j ret_from_irq + nop + +1: jal do_IRQ + move a1, sp + + j ret_from_irq + nop + +spurious: + j spurious_interrupt + nop + END(mipsIRQ) diff --git a/arch/mips/mips-boards/generic/Makefile b/arch/mips/mips-boards/generic/Makefile index b21bc6887fa8..be47c1c2bc80 100644 --- a/arch/mips/mips-boards/generic/Makefile +++ b/arch/mips/mips-boards/generic/Makefile @@ -18,8 +18,8 @@ # Makefile for the MIPS boards generic routines under Linux. # -obj-y := mipsIRQ.o reset.o display.o init.o memory.o \ - printf.o cmdline.o time.o +obj-y := reset.o display.o init.o memory.o printf.o \ + cmdline.o time.o obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_KGDB) += gdb_hook.o diff --git a/arch/mips/mips-boards/generic/mipsIRQ.S b/arch/mips/mips-boards/generic/mipsIRQ.S deleted file mode 100644 index 973e10aaacd5..000000000000 --- a/arch/mips/mips-boards/generic/mipsIRQ.S +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Carsten Langgaard, carstenl@mips.com - * Copyright (C) 1999, 2000 MIPS Technologies, Inc. All rights reserved. - * - * ######################################################################## - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * ######################################################################## - * - * Interrupt exception dispatch code. - * - */ -#include - -#include -#include -#include -#include - -#ifdef CONFIG_MIPS_ATLAS -#include -#define CASCADE_IRQ MIPSCPU_INT_ATLAS -#define CASCADE_DISPATCH atlas_hw0_irqdispatch -#endif -#ifdef CONFIG_MIPS_MALTA -#include -#define CASCADE_IRQ MIPSCPU_INT_I8259A -#define CASCADE_DISPATCH malta_hw0_irqdispatch -#endif -#ifdef CONFIG_MIPS_SEAD -#include -#endif - -/* A lot of complication here is taken away because: - * - * 1) We handle one interrupt and return, sitting in a loop and moving across - * all the pending IRQ bits in the cause register is _NOT_ the answer, the - * common case is one pending IRQ so optimize in that direction. - * - * 2) We need not check against bits in the status register IRQ mask, that - * would make this routine slow as hell. - * - * 3) Linux only thinks in terms of all IRQs on or all IRQs off, nothing in - * between like BSD spl() brain-damage. - * - * Furthermore, the IRQs on the MIPS board look basically (barring software - * IRQs which we don't use at all and all external interrupt sources are - * combined together on hardware interrupt 0 (MIPS IRQ 2)) like: - * - * MIPS IRQ Source - * -------- ------ - * 0 Software (ignored) - * 1 Software (ignored) - * 2 Combined hardware interrupt (hw0) - * 3 Hardware (ignored) - * 4 Hardware (ignored) - * 5 Hardware (ignored) - * 6 Hardware (ignored) - * 7 R4k timer (what we use) - * - * Note: On the SEAD board thing are a little bit different. - * Here IRQ 2 (hw0) is wired to the UART0 and IRQ 3 (hw1) is wired - * wired to UART1. - * - * We handle the IRQ according to _our_ priority which is: - * - * Highest ---- R4k Timer - * Lowest ---- Combined hardware interrupt - * - * then we just return, if multiple IRQs are pending then we will just take - * another exception, big deal. - */ - - .text - .set noreorder - .set noat - .align 5 - NESTED(mipsIRQ, PT_SIZE, sp) - SAVE_ALL - CLI - .set at - - mfc0 s0, CP0_CAUSE # get irq bits - mfc0 s1, CP0_STATUS # get irq mask - andi s0, ST0_IM # CAUSE.CE may be non-zero! - and s0, s1 - -#if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) - .set mips32 - clz a0, s0 - .set mips0 - negu a0 - addu a0, 31-CAUSEB_IP - bltz a0, spurious -#else - beqz s0, spurious - li a0, 7 - - and t0, s0, 0xf000 - sltiu t0, t0, 1 - sll t0, 2 - subu a0, t0 - sll s0, t0 - - and t0, s0, 0xc000 - sltiu t0, t0, 1 - sll t0, 1 - subu a0, t0 - sll s0, t0 - - and t0, s0, 0x8000 - sltiu t0, t0, 1 - # sll t0, 0 - subu a0, t0 - # sll s0, t0 -#endif - -#ifdef CASCADE_IRQ - li a1, CASCADE_IRQ - bne a0, a1, 1f - addu a0, MIPSCPU_INT_BASE - - jal CASCADE_DISPATCH - move a0, sp - - j ret_from_irq - nop -1: -#else - addu a0, MIPSCPU_INT_BASE -#endif - - jal do_IRQ - move a1, sp - - j ret_from_irq - nop - - -spurious: - jal spurious_interrupt - nop - j ret_from_irq - nop - END(mipsIRQ) diff --git a/arch/mips/mips-boards/malta/Makefile b/arch/mips/mips-boards/malta/Makefile index fd4c143c0e2f..3ae8fe6c0070 100644 --- a/arch/mips/mips-boards/malta/Makefile +++ b/arch/mips/mips-boards/malta/Makefile @@ -19,4 +19,4 @@ # under Linux. # -obj-y := malta_int.o malta_setup.o +obj-y := malta_int.o malta-irq.o malta_setup.o diff --git a/arch/mips/mips-boards/malta/malta-irq.S b/arch/mips/mips-boards/malta/malta-irq.S new file mode 100644 index 000000000000..6217aff3be03 --- /dev/null +++ b/arch/mips/mips-boards/malta/malta-irq.S @@ -0,0 +1,122 @@ +/* + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999, 2000 MIPS Technologies, Inc. All rights reserved. + * + * ######################################################################## + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * ######################################################################## + * + * Interrupt exception dispatch code. + * + */ +#include + +#include +#include +#include +#include +#include + +/* + * IRQs on the Malta board look basically (barring software IRQs which we + * don't use at all and all external interrupt sources are combined together + * on hardware interrupt 0 (MIPS IRQ 2)) like: + * + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 Combined hardware interrupt (hw0) + * 3 Hardware (ignored) + * 4 Hardware (ignored) + * 5 Hardware (ignored) + * 6 Hardware (ignored) + * 7 R4k timer (what we use) + * + * We handle the IRQ according to _our_ priority which is: + * + * Highest ---- R4k Timer + * Lowest ---- Combined hardware interrupt + * + * then we just return, if multiple IRQs are pending then we will just take + * another exception, big deal. + */ + + .text + .set noreorder + .set noat + .align 5 + NESTED(mipsIRQ, PT_SIZE, sp) + SAVE_ALL + CLI + .set at + + mfc0 s0, CP0_CAUSE # get irq bits + mfc0 s1, CP0_STATUS # get irq mask + andi s0, ST0_IM # CAUSE.CE may be non-zero! + and s0, s1 + +#if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) + .set mips32 + clz a0, s0 + .set mips0 + negu a0 + addu a0, 31-CAUSEB_IP + bltz a0, spurious +#else + beqz s0, spurious + li a0, 7 + + and t0, s0, 0xf000 + sltiu t0, t0, 1 + sll t0, 2 + subu a0, t0 + sll s0, t0 + + and t0, s0, 0xc000 + sltiu t0, t0, 1 + sll t0, 1 + subu a0, t0 + sll s0, t0 + + and t0, s0, 0x8000 + sltiu t0, t0, 1 + # sll t0, 0 + subu a0, t0 + # sll s0, t0 +#endif + + li a1, MIPSCPU_INT_I8259A + bne a0, a1, 1f + addu a0, MIPSCPU_INT_BASE + + jal malta_hw0_irqdispatch + move a0, sp + + j ret_from_irq + nop +1: + + jal do_IRQ + move a1, sp + + j ret_from_irq + nop + +spurious: + j spurious_interrupt + nop + END(mipsIRQ) diff --git a/arch/mips/mips-boards/sead/Makefile b/arch/mips/mips-boards/sead/Makefile index 224bb848f16b..01780b605346 100644 --- a/arch/mips/mips-boards/sead/Makefile +++ b/arch/mips/mips-boards/sead/Makefile @@ -23,4 +23,4 @@ # under Linux. # -obj-y := sead_int.o sead_setup.o +obj-y := sead_int.o sead-irq.o sead_setup.o diff --git a/arch/mips/mips-boards/sead/sead-irq.S b/arch/mips/mips-boards/sead/sead-irq.S new file mode 100644 index 000000000000..d5dea1d2e220 --- /dev/null +++ b/arch/mips/mips-boards/sead/sead-irq.S @@ -0,0 +1,111 @@ +/* + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999, 2000 MIPS Technologies, Inc. All rights reserved. + * + * ######################################################################## + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * ######################################################################## + * + * Interrupt exception dispatch code. + * + */ +#include + +#include +#include +#include +#include +#include + +/* + * IRQs on the SEAD board look basically are combined together on hardware + * interrupt 0 (MIPS IRQ 2)) like: + * + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 UART0 (hw0) + * 3 UART1 (hw1) + * 4 Hardware (ignored) + * 5 Hardware (ignored) + * 6 Hardware (ignored) + * 7 R4k timer (what we use) + * + * We handle the IRQ according to _our_ priority which is: + * + * Highest ---- R4k Timer + * Lowest ---- Combined hardware interrupt + * + * then we just return, if multiple IRQs are pending then we will just take + * another exception, big deal. + */ + + .text + .set noreorder + .set noat + .align 5 + NESTED(mipsIRQ, PT_SIZE, sp) + SAVE_ALL + CLI + .set at + + mfc0 s0, CP0_CAUSE # get irq bits + mfc0 s1, CP0_STATUS # get irq mask + andi s0, ST0_IM # CAUSE.CE may be non-zero! + and s0, s1 + +#if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) + .set mips32 + clz a0, s0 + .set mips0 + negu a0 + addu a0, 31-CAUSEB_IP + bltz a0, spurious +#else + beqz s0, spurious + li a0, 7 + + and t0, s0, 0xf000 + sltiu t0, t0, 1 + sll t0, 2 + subu a0, t0 + sll s0, t0 + + and t0, s0, 0xc000 + sltiu t0, t0, 1 + sll t0, 1 + subu a0, t0 + sll s0, t0 + + and t0, s0, 0x8000 + sltiu t0, t0, 1 + # sll t0, 0 + subu a0, t0 + # sll s0, t0 +#endif + + addu a0, MIPSCPU_INT_BASE + jal do_IRQ + move a1, sp + + j ret_from_irq + nop + +spurious: + j spurious_interrupt + nop + END(mipsIRQ) diff --git a/include/asm-mips/mips-boards/atlas.h b/include/asm-mips/mips-boards/atlas.h index 0998151fb3a1..a8ae12d120ee 100644 --- a/include/asm-mips/mips-boards/atlas.h +++ b/include/asm-mips/mips-boards/atlas.h @@ -33,12 +33,28 @@ #define ATLAS_RTC_ADR_REG 0x1f000800 #define ATLAS_RTC_DAT_REG 0x1f000808 - /* * Atlas interrupt controller register base. */ #define ATLAS_ICTRL_REGS_BASE 0x1f000000 +/* + * Atlas registers are memory mapped on 64-bit aligned boundaries and + * only word access are allowed. + */ +struct atlas_ictrl_regs { + volatile unsigned int intraw; + int dummy1; + volatile unsigned int intseten; + int dummy2; + volatile unsigned int intrsten; + int dummy3; + volatile unsigned int intenable; + int dummy4; + volatile unsigned int intstatus; + int dummy5; +}; + /* * Atlas UART register base. */ diff --git a/include/asm-mips/mips-boards/atlasint.h b/include/asm-mips/mips-boards/atlasint.h index bba35c183d08..fd7ebc54fa90 100644 --- a/include/asm-mips/mips-boards/atlasint.h +++ b/include/asm-mips/mips-boards/atlasint.h @@ -62,23 +62,4 @@ #define ATLASINT_RES31 (ATLASINT_BASE+31) #define ATLASINT_END (ATLASINT_BASE+31) -/* - * Atlas registers are memory mapped on 64-bit aligned boundaries and - * only word access are allowed. - */ -struct atlas_ictrl_regs { - volatile unsigned int intraw; - int dummy1; - volatile unsigned int intseten; - int dummy2; - volatile unsigned int intrsten; - int dummy3; - volatile unsigned int intenable; - int dummy4; - volatile unsigned int intstatus; - int dummy5; -}; - -extern void atlasint_init(void); - #endif /* !(_MIPS_ATLASINT_H) */ -- cgit v1.2.3 From 675055bfb5f99be56a20a6a214439adf23591786 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Mon, 3 Apr 2006 23:32:39 +0100 Subject: [MIPS] Use "R" constraint for cache_op. Gcc might emit an absolute address for the the "m" constraint which gas unfortunately does not permit. Signed-off-by: Ralf Baechle --- include/asm-mips/r4kcache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-mips/r4kcache.h b/include/asm-mips/r4kcache.h index 90c374700977..2f2eb95387f6 100644 --- a/include/asm-mips/r4kcache.h +++ b/include/asm-mips/r4kcache.h @@ -37,7 +37,7 @@ " cache %0, %1 \n" \ " .set pop \n" \ : \ - : "i" (op), "m" (*(unsigned char *)(addr))) + : "i" (op), "R" (*(unsigned char *)(addr))) static inline void flush_icache_line_indexed(unsigned long addr) { -- cgit v1.2.3 From a682a2417007ad6265cd71b97b751753fd10e2fb Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 4 Apr 2006 16:59:37 +0100 Subject: [MIPS] Fix genrtc compilation. Signed-off-by: Ralf Roesch Signed-off-by: Ralf Baechle --- include/asm-mips/rtc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-mips/rtc.h b/include/asm-mips/rtc.h index a2abc4572b63..82ad401c7dca 100644 --- a/include/asm-mips/rtc.h +++ b/include/asm-mips/rtc.h @@ -32,7 +32,7 @@ static inline unsigned int get_rtc_time(struct rtc_time *time) { unsigned long nowtime; - nowtime = rtc_get_time(); + nowtime = rtc_mips_get_time(); to_tm(nowtime, time); time->tm_year -= 1900; @@ -47,7 +47,7 @@ static inline int set_rtc_time(struct rtc_time *time) nowtime = mktime(time->tm_year+1900, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec); - ret = rtc_set_time(nowtime); + ret = rtc_mips_set_time(nowtime); return ret; } -- cgit v1.2.3 From 2600990e640e3bef29ed89d565864cf16ee83833 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 5 Apr 2006 09:45:45 +0100 Subject: [MIPS] kpsd and other AP/SP improvements. Signed-off-by: Ralf Baechle --- arch/mips/Kconfig | 10 + arch/mips/kernel/Makefile | 1 + arch/mips/kernel/kspd.c | 398 +++++++++++++++++++++++++ arch/mips/kernel/rtlx.c | 517 +++++++++++++++++++++++---------- arch/mips/kernel/vpe.c | 659 ++++++++++++++++++++++++++++-------------- include/asm-mips/kspd.h | 36 +++ include/asm-mips/mipsmtregs.h | 5 +- include/asm-mips/rtlx.h | 38 ++- include/asm-mips/vpe.h | 37 +++ 9 files changed, 1307 insertions(+), 394 deletions(-) create mode 100644 arch/mips/kernel/kspd.c create mode 100644 include/asm-mips/kspd.h create mode 100644 include/asm-mips/vpe.h (limited to 'include') diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 7b49aa5e8e2b..a7bac0459f99 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1476,6 +1476,16 @@ config MIPS_VPE_APSP_API depends on MIPS_VPE_LOADER help +config MIPS_APSP_KSPD + bool "Enable KSPD" + depends on MIPS_VPE_APSP_API + default y + help + KSPD is a kernel daemon that accepts syscall requests from the SP + side, actions them and returns the results. It also handles the + "exit" syscall notifying other kernel modules the SP program is + exiting. You probably want to say yes here. + config SB1_PASS_1_WORKAROUNDS bool depends on CPU_SB1_PASS_1 diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 309d54cceda3..9ec01de81c04 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_MIPS_MT_SMP) += smp_mt.o +obj-$(CONFIG_MIPS_APSP_KSPD) += kspd.o obj-$(CONFIG_MIPS_VPE_LOADER) += vpe.o obj-$(CONFIG_MIPS_VPE_APSP_API) += rtlx.o diff --git a/arch/mips/kernel/kspd.c b/arch/mips/kernel/kspd.c new file mode 100644 index 000000000000..f06a144c7881 --- /dev/null +++ b/arch/mips/kernel/kspd.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct workqueue_struct *workqueue = NULL; +static struct work_struct work; + +extern unsigned long cpu_khz; + +struct mtsp_syscall { + int cmd; + unsigned char abi; + unsigned char size; +}; + +struct mtsp_syscall_ret { + int retval; + int errno; +}; + +struct mtsp_syscall_generic { + int arg0; + int arg1; + int arg2; + int arg3; + int arg4; + int arg5; + int arg6; +}; + +static struct list_head kspd_notifylist; +static int sp_stopping = 0; + +/* these should match with those in the SDE kit */ +#define MTSP_SYSCALL_BASE 0 +#define MTSP_SYSCALL_EXIT (MTSP_SYSCALL_BASE + 0) +#define MTSP_SYSCALL_OPEN (MTSP_SYSCALL_BASE + 1) +#define MTSP_SYSCALL_READ (MTSP_SYSCALL_BASE + 2) +#define MTSP_SYSCALL_WRITE (MTSP_SYSCALL_BASE + 3) +#define MTSP_SYSCALL_CLOSE (MTSP_SYSCALL_BASE + 4) +#define MTSP_SYSCALL_LSEEK32 (MTSP_SYSCALL_BASE + 5) +#define MTSP_SYSCALL_ISATTY (MTSP_SYSCALL_BASE + 6) +#define MTSP_SYSCALL_GETTIME (MTSP_SYSCALL_BASE + 7) +#define MTSP_SYSCALL_PIPEFREQ (MTSP_SYSCALL_BASE + 8) +#define MTSP_SYSCALL_GETTOD (MTSP_SYSCALL_BASE + 9) + +#define MTSP_O_RDONLY 0x0000 +#define MTSP_O_WRONLY 0x0001 +#define MTSP_O_RDWR 0x0002 +#define MTSP_O_NONBLOCK 0x0004 +#define MTSP_O_APPEND 0x0008 +#define MTSP_O_SHLOCK 0x0010 +#define MTSP_O_EXLOCK 0x0020 +#define MTSP_O_ASYNC 0x0040 +#define MTSP_O_FSYNC O_SYNC +#define MTSP_O_NOFOLLOW 0x0100 +#define MTSP_O_SYNC 0x0080 +#define MTSP_O_CREAT 0x0200 +#define MTSP_O_TRUNC 0x0400 +#define MTSP_O_EXCL 0x0800 +#define MTSP_O_BINARY 0x8000 + +#define SP_VPE 1 + +struct apsp_table { + int sp; + int ap; +}; + +/* we might want to do the mode flags too */ +struct apsp_table open_flags_table[] = { + { MTSP_O_RDWR, O_RDWR }, + { MTSP_O_WRONLY, O_WRONLY }, + { MTSP_O_CREAT, O_CREAT }, + { MTSP_O_TRUNC, O_TRUNC }, + { MTSP_O_NONBLOCK, O_NONBLOCK }, + { MTSP_O_APPEND, O_APPEND }, + { MTSP_O_NOFOLLOW, O_NOFOLLOW } +}; + +struct apsp_table syscall_command_table[] = { + { MTSP_SYSCALL_OPEN, __NR_open }, + { MTSP_SYSCALL_CLOSE, __NR_close }, + { MTSP_SYSCALL_READ, __NR_read }, + { MTSP_SYSCALL_WRITE, __NR_write }, + { MTSP_SYSCALL_LSEEK32, __NR_lseek } +}; + +static int sp_syscall(int num, int arg0, int arg1, int arg2, int arg3) +{ + register long int _num __asm__ ("$2") = num; + register long int _arg0 __asm__ ("$4") = arg0; + register long int _arg1 __asm__ ("$5") = arg1; + register long int _arg2 __asm__ ("$6") = arg2; + register long int _arg3 __asm__ ("$7") = arg3; + + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + __asm__ __volatile__ ( + " syscall \n" + : "=r" (_num), "=r" (_arg3) + : "r" (_num), "r" (_arg0), "r" (_arg1), "r" (_arg2), "r" (_arg3)); + + set_fs(old_fs); + + /* $a3 is error flag */ + if (_arg3) + return -_num; + + return _num; +} + +static int translate_syscall_command(int cmd) +{ + int i; + int ret = -1; + + for (i = 0; i < ARRAY_SIZE(syscall_command_table); i++) { + if ((cmd == syscall_command_table[i].sp)) + return syscall_command_table[i].ap; + } + + return ret; +} + +static unsigned int translate_open_flags(int flags) +{ + int i; + unsigned int ret = 0; + + for (i = 0; i < (sizeof(open_flags_table) / sizeof(struct apsp_table)); + i++) { + if( (flags & open_flags_table[i].sp) ) { + ret |= open_flags_table[i].ap; + } + } + + return ret; +} + + +static void sp_setfsuidgid( uid_t uid, gid_t gid) +{ + current->fsuid = uid; + current->fsgid = gid; + + key_fsuid_changed(current); + key_fsgid_changed(current); +} + +/* + * Expects a request to be on the sysio channel. Reads it. Decides whether + * its a linux syscall and runs it, or whatever. Puts the return code back + * into the request and sends the whole thing back. + */ +void sp_work_handle_request(void) +{ + struct mtsp_syscall sc; + struct mtsp_syscall_generic generic; + struct mtsp_syscall_ret ret; + struct kspd_notifications *n; + struct timeval tv; + struct timezone tz; + int cmd; + + char *vcwd; + mm_segment_t old_fs; + int size; + + ret.retval = -1; + + if (!rtlx_read(RTLX_CHANNEL_SYSIO, &sc, sizeof(struct mtsp_syscall), 0)) { + printk(KERN_ERR "Expected request but nothing to read\n"); + return; + } + + size = sc.size; + + if (size) { + if (!rtlx_read(RTLX_CHANNEL_SYSIO, &generic, size, 0)) { + printk(KERN_ERR "Expected request but nothing to read\n"); + return; + } + } + + /* Run the syscall at the priviledge of the user who loaded the + SP program */ + + if (vpe_getuid(SP_VPE)) + sp_setfsuidgid( vpe_getuid(SP_VPE), vpe_getgid(SP_VPE)); + + switch (sc.cmd) { + /* needs the flags argument translating from SDE kit to + linux */ + case MTSP_SYSCALL_PIPEFREQ: + ret.retval = cpu_khz * 1000; + ret.errno = 0; + break; + + case MTSP_SYSCALL_GETTOD: + memset(&tz, 0, sizeof(tz)); + if ((ret.retval = sp_syscall(__NR_gettimeofday, (int)&tv, + (int)&tz, 0,0)) == 0) + ret.retval = tv.tv_sec; + + ret.errno = errno; + break; + + case MTSP_SYSCALL_EXIT: + list_for_each_entry(n, &kspd_notifylist, list) + n->kspd_sp_exit(SP_VPE); + sp_stopping = 1; + + printk(KERN_DEBUG "KSPD got exit syscall from SP exitcode %d\n", + generic.arg0); + break; + + case MTSP_SYSCALL_OPEN: + generic.arg1 = translate_open_flags(generic.arg1); + + vcwd = vpe_getcwd(SP_VPE); + + /* change to the cwd of the process that loaded the SP program */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + sys_chdir(vcwd); + set_fs(old_fs); + + sc.cmd = __NR_open; + + /* fall through */ + + default: + if ((sc.cmd >= __NR_Linux) && + (sc.cmd <= (__NR_Linux + __NR_Linux_syscalls)) ) + cmd = sc.cmd; + else + cmd = translate_syscall_command(sc.cmd); + + if (cmd >= 0) { + ret.retval = sp_syscall(cmd, generic.arg0, generic.arg1, + generic.arg2, generic.arg3); + ret.errno = errno; + } else + printk(KERN_WARNING + "KSPD: Unknown SP syscall number %d\n", sc.cmd); + break; + } /* switch */ + + if (vpe_getuid(SP_VPE)) + sp_setfsuidgid( 0, 0); + + if ((rtlx_write(RTLX_CHANNEL_SYSIO, &ret, sizeof(struct mtsp_syscall_ret), 0)) + < sizeof(struct mtsp_syscall_ret)) + printk("KSPD: sp_work_handle_request failed to send to SP\n"); +} + +static void sp_cleanup(void) +{ + struct files_struct *files = current->files; + int i, j; + struct fdtable *fdt; + + j = 0; + + /* + * It is safe to dereference the fd table without RCU or + * ->file_lock + */ + fdt = files_fdtable(files); + for (;;) { + unsigned long set; + i = j * __NFDBITS; + if (i >= fdt->max_fdset || i >= fdt->max_fds) + break; + set = fdt->open_fds->fds_bits[j++]; + while (set) { + if (set & 1) { + struct file * file = xchg(&fdt->fd[i], NULL); + if (file) + filp_close(file, files); + } + i++; + set >>= 1; + } + } +} + +static int channel_open = 0; + +/* the work handler */ +static void sp_work(void *data) +{ + if (!channel_open) { + if( rtlx_open(RTLX_CHANNEL_SYSIO, 1) != 0) { + printk("KSPD: unable to open sp channel\n"); + sp_stopping = 1; + } else { + channel_open++; + printk(KERN_DEBUG "KSPD: SP channel opened\n"); + } + } else { + /* wait for some data, allow it to sleep */ + rtlx_read_poll(RTLX_CHANNEL_SYSIO, 1); + + /* Check we haven't been woken because we are stopping */ + if (!sp_stopping) + sp_work_handle_request(); + } + + if (!sp_stopping) + queue_work(workqueue, &work); + else + sp_cleanup(); +} + +static void startwork(int vpe) +{ + sp_stopping = channel_open = 0; + + if (workqueue == NULL) { + if ((workqueue = create_singlethread_workqueue("kspd")) == NULL) { + printk(KERN_ERR "unable to start kspd\n"); + return; + } + + INIT_WORK(&work, sp_work, NULL); + queue_work(workqueue, &work); + } else + queue_work(workqueue, &work); + +} + +static void stopwork(int vpe) +{ + sp_stopping = 1; + + printk(KERN_DEBUG "KSPD: SP stopping\n"); +} + +void kspd_notify(struct kspd_notifications *notify) +{ + list_add(¬ify->list, &kspd_notifylist); +} + +static struct vpe_notifications notify; +static int kspd_module_init(void) +{ + INIT_LIST_HEAD(&kspd_notifylist); + + notify.start = startwork; + notify.stop = stopwork; + vpe_notify(SP_VPE, ¬ify); + + return 0; +} + +static void kspd_module_exit(void) +{ + +} + +module_init(kspd_module_init); +module_exit(kspd_module_exit); + +MODULE_DESCRIPTION("MIPS KSPD"); +MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc."); +MODULE_LICENSE("GPL"); diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index 986a9cf23067..6179805af9f0 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c @@ -21,45 +21,44 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include #include #include #include - #include -#include +#include +#include #include #include +#include +#include #include -#include #define RTLX_TARG_VPE 1 static struct rtlx_info *rtlx; static int major; static char module_name[] = "rtlx"; -static struct irqaction irq; -static int irq_num; - -static inline int spacefree(int read, int write, int size) -{ - if (read == write) { - /* - * never fill the buffer completely, so indexes are always - * equal if empty and only empty, or !equal if data available - */ - return size - 1; - } - - return ((read + size - write) % size) - 1; -} static struct chan_waitqueues { wait_queue_head_t rt_queue; wait_queue_head_t lx_queue; + int in_open; } channel_wqs[RTLX_CHANNELS]; +static struct irqaction irq; +static int irq_num; +static struct vpe_notifications notify; +static int sp_stopping = 0; + extern void *vpe_get_shared(int index); static void rtlx_dispatch(struct pt_regs *regs) @@ -67,174 +66,298 @@ static void rtlx_dispatch(struct pt_regs *regs) do_IRQ(MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ, regs); } + +/* Interrupt handler may be called before rtlx_init has otherwise had + a chance to run. +*/ static irqreturn_t rtlx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { int i; for (i = 0; i < RTLX_CHANNELS; i++) { - struct rtlx_channel *chan = &rtlx->channel[i]; - - if (chan->lx_read != chan->lx_write) - wake_up_interruptible(&channel_wqs[i].lx_queue); + wake_up(&channel_wqs[i].lx_queue); + wake_up(&channel_wqs[i].rt_queue); } return IRQ_HANDLED; } -/* call when we have the address of the shared structure from the SP side. */ -static int rtlx_init(struct rtlx_info *rtlxi) +static __attribute_used__ void dump_rtlx(void) { int i; - if (rtlxi->id != RTLX_ID) { - printk(KERN_WARNING "no valid RTLX id at 0x%p\n", rtlxi); - return -ENOEXEC; - } + printk("id 0x%lx state %d\n", rtlx->id, rtlx->state); - /* initialise the wait queues */ for (i = 0; i < RTLX_CHANNELS; i++) { - init_waitqueue_head(&channel_wqs[i].rt_queue); - init_waitqueue_head(&channel_wqs[i].lx_queue); - } + struct rtlx_channel *chan = &rtlx->channel[i]; - /* set up for interrupt handling */ - memset(&irq, 0, sizeof(struct irqaction)); + printk(" rt_state %d lx_state %d buffer_size %d\n", + chan->rt_state, chan->lx_state, chan->buffer_size); - if (cpu_has_vint) - set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch); + printk(" rt_read %d rt_write %d\n", + chan->rt_read, chan->rt_write); - irq_num = MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ; - irq.handler = rtlx_interrupt; - irq.flags = SA_INTERRUPT; - irq.name = "RTLX"; - irq.dev_id = rtlx; - setup_irq(irq_num, &irq); + printk(" lx_read %d lx_write %d\n", + chan->lx_read, chan->lx_write); + + printk(" rt_buffer <%s>\n", chan->rt_buffer); + printk(" lx_buffer <%s>\n", chan->lx_buffer); + } +} + +/* call when we have the address of the shared structure from the SP side. */ +static int rtlx_init(struct rtlx_info *rtlxi) +{ + if (rtlxi->id != RTLX_ID) { + printk(KERN_ERR "no valid RTLX id at 0x%p 0x%x\n", rtlxi, rtlxi->id); + return -ENOEXEC; + } rtlx = rtlxi; return 0; } -/* only allow one open process at a time to open each channel */ -static int rtlx_open(struct inode *inode, struct file *filp) +/* notifications */ +static void starting(int vpe) { - int minor, ret; + int i; + sp_stopping = 0; + + /* force a reload of rtlx */ + rtlx=NULL; + + /* wake up any sleeping rtlx_open's */ + for (i = 0; i < RTLX_CHANNELS; i++) + wake_up_interruptible(&channel_wqs[i].lx_queue); +} + +static void stopping(int vpe) +{ + int i; + + sp_stopping = 1; + for (i = 0; i < RTLX_CHANNELS; i++) + wake_up_interruptible(&channel_wqs[i].lx_queue); +} + + +int rtlx_open(int index, int can_sleep) +{ + int ret; struct rtlx_channel *chan; + volatile struct rtlx_info **p; - /* assume only 1 device at the mo. */ - minor = MINOR(inode->i_rdev); + if (index >= RTLX_CHANNELS) { + printk(KERN_DEBUG "rtlx_open index out of range\n"); + return -ENOSYS; + } + + if (channel_wqs[index].in_open) { + printk(KERN_DEBUG "rtlx_open channel %d already opened\n", index); + return -EBUSY; + } + + channel_wqs[index].in_open++; if (rtlx == NULL) { - struct rtlx_info **p; if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) { - printk(KERN_ERR "vpe_get_shared is NULL. " - "Has an SP program been loaded?\n"); - return -EFAULT; + if (can_sleep) { + DECLARE_WAITQUEUE(wait, current); + + /* go to sleep */ + add_wait_queue(&channel_wqs[index].lx_queue, &wait); + + set_current_state(TASK_INTERRUPTIBLE); + while ((p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) { + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&channel_wqs[index].lx_queue, &wait); + + /* back running */ + } else { + printk( KERN_DEBUG "No SP program loaded, and device " + "opened with O_NONBLOCK\n"); + channel_wqs[index].in_open = 0; + return -ENOSYS; + } } if (*p == NULL) { - printk(KERN_ERR "vpe_shared %p %p\n", p, *p); - return -EFAULT; + if (can_sleep) { + DECLARE_WAITQUEUE(wait, current); + + /* go to sleep */ + add_wait_queue(&channel_wqs[index].lx_queue, &wait); + + set_current_state(TASK_INTERRUPTIBLE); + while (*p == NULL) { + schedule(); + + /* reset task state to interruptable otherwise + we'll whizz round here like a very fast loopy + thing. schedule() appears to return with state + set to TASK_RUNNING. + + If the loaded SP program, for whatever reason, + doesn't set up the shared structure *p will never + become true. So whoever connected to either /dev/rt? + or if it was kspd, will then take up rather a lot of + processor cycles. + */ + + set_current_state(TASK_INTERRUPTIBLE); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&channel_wqs[index].lx_queue, &wait); + + /* back running */ + } + else { + printk(" *vpe_get_shared is NULL. " + "Has an SP program been loaded?\n"); + channel_wqs[index].in_open = 0; + return -ENOSYS; + } + } + + if ((unsigned int)*p < KSEG0) { + printk(KERN_WARNING "vpe_get_shared returned an invalid pointer " + "maybe an error code %d\n", (int)*p); + channel_wqs[index].in_open = 0; + return -ENOSYS; } - if ((ret = rtlx_init(*p)) < 0) - return ret; + if ((ret = rtlx_init(*p)) < 0) { + channel_wqs[index].in_open = 0; + return ret; + } } - chan = &rtlx->channel[minor]; + chan = &rtlx->channel[index]; - if (test_and_set_bit(RTLX_STATE_OPENED, &chan->lx_state)) - return -EBUSY; + if (chan->lx_state == RTLX_STATE_OPENED) { + channel_wqs[index].in_open = 0; + return -EBUSY; + } + chan->lx_state = RTLX_STATE_OPENED; + channel_wqs[index].in_open = 0; return 0; } -static int rtlx_release(struct inode *inode, struct file *filp) +int rtlx_release(int index) { - int minor = MINOR(inode->i_rdev); - - clear_bit(RTLX_STATE_OPENED, &rtlx->channel[minor].lx_state); - smp_mb__after_clear_bit(); - + rtlx->channel[index].lx_state = RTLX_STATE_UNUSED; return 0; } -static unsigned int rtlx_poll(struct file *file, poll_table * wait) +unsigned int rtlx_read_poll(int index, int can_sleep) { - int minor; - unsigned int mask = 0; - struct rtlx_channel *chan; + struct rtlx_channel *chan; - minor = MINOR(file->f_dentry->d_inode->i_rdev); - chan = &rtlx->channel[minor]; + if (rtlx == NULL) + return 0; - poll_wait(file, &channel_wqs[minor].rt_queue, wait); - poll_wait(file, &channel_wqs[minor].lx_queue, wait); + chan = &rtlx->channel[index]; /* data available to read? */ - if (chan->lx_read != chan->lx_write) - mask |= POLLIN | POLLRDNORM; + if (chan->lx_read == chan->lx_write) { + if (can_sleep) { + DECLARE_WAITQUEUE(wait, current); - /* space to write */ - if (spacefree(chan->rt_read, chan->rt_write, chan->buffer_size)) - mask |= POLLOUT | POLLWRNORM; + /* go to sleep */ + add_wait_queue(&channel_wqs[index].lx_queue, &wait); - return mask; + set_current_state(TASK_INTERRUPTIBLE); + while (chan->lx_read == chan->lx_write) { + schedule(); + + set_current_state(TASK_INTERRUPTIBLE); + + if (sp_stopping) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&channel_wqs[index].lx_queue, &wait); + return 0; + } + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&channel_wqs[index].lx_queue, &wait); + + /* back running */ + } + else + return 0; + } + + return (chan->lx_write + chan->buffer_size - chan->lx_read) + % chan->buffer_size; } -static ssize_t rtlx_read(struct file *file, char __user * buffer, size_t count, - loff_t * ppos) +static inline int write_spacefree(int read, int write, int size) { - unsigned long failed; - size_t fl = 0L; - int minor; - struct rtlx_channel *lx; - DECLARE_WAITQUEUE(wait, current); + if (read == write) { + /* + * Never fill the buffer completely, so indexes are always + * equal if empty and only empty, or !equal if data available + */ + return size - 1; + } - minor = MINOR(file->f_dentry->d_inode->i_rdev); - lx = &rtlx->channel[minor]; + return ((read + size - write) % size) - 1; +} - /* data available? */ - if (lx->lx_write == lx->lx_read) { - if (file->f_flags & O_NONBLOCK) - return 0; /* -EAGAIN makes cat whinge */ +unsigned int rtlx_write_poll(int index) +{ + struct rtlx_channel *chan = &rtlx->channel[index]; + return write_spacefree(chan->rt_read, chan->rt_write, chan->buffer_size); +} - /* go to sleep */ - add_wait_queue(&channel_wqs[minor].lx_queue, &wait); - set_current_state(TASK_INTERRUPTIBLE); +static inline void copy_to(void *dst, void *src, size_t count, int user) +{ + if (user) + copy_to_user(dst, src, count); + else + memcpy(dst, src, count); +} - while (lx->lx_write == lx->lx_read) - schedule(); +static inline void copy_from(void *dst, void *src, size_t count, int user) +{ + if (user) + copy_from_user(dst, src, count); + else + memcpy(dst, src, count); +} - set_current_state(TASK_RUNNING); - remove_wait_queue(&channel_wqs[minor].lx_queue, &wait); +ssize_t rtlx_read(int index, void *buff, size_t count, int user) +{ + size_t fl = 0L; + struct rtlx_channel *lx; - /* back running */ - } + if (rtlx == NULL) + return -ENOSYS; + + lx = &rtlx->channel[index]; /* find out how much in total */ count = min(count, - (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read) % lx->buffer_size); + (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read) + % lx->buffer_size); /* then how much from the read pointer onwards */ - fl = min(count, (size_t)lx->buffer_size - lx->lx_read); + fl = min( count, (size_t)lx->buffer_size - lx->lx_read); - failed = copy_to_user (buffer, &lx->lx_buffer[lx->lx_read], fl); - if (failed) { - count = fl - failed; - goto out; - } + copy_to(buff, &lx->lx_buffer[lx->lx_read], fl, user); /* and if there is anything left at the beginning of the buffer */ - if (count - fl) { - failed = copy_to_user (buffer + fl, lx->lx_buffer, count - fl); - if (failed) { - count -= failed; - goto out; - } - } + if ( count - fl ) + copy_to (buff + fl, lx->lx_buffer, count - fl, user); -out: /* update the index */ lx->lx_read += count; lx->lx_read %= lx->buffer_size; @@ -242,20 +365,101 @@ out: return count; } -static ssize_t rtlx_write(struct file *file, const char __user * buffer, +ssize_t rtlx_write(int index, void *buffer, size_t count, int user) +{ + struct rtlx_channel *rt; + size_t fl; + + if (rtlx == NULL) + return(-ENOSYS); + + rt = &rtlx->channel[index]; + + /* total number of bytes to copy */ + count = min(count, + (size_t)write_spacefree(rt->rt_read, rt->rt_write, + rt->buffer_size)); + + /* first bit from write pointer to the end of the buffer, or count */ + fl = min(count, (size_t) rt->buffer_size - rt->rt_write); + + copy_from (&rt->rt_buffer[rt->rt_write], buffer, fl, user); + + /* if there's any left copy to the beginning of the buffer */ + if( count - fl ) + copy_from (rt->rt_buffer, buffer + fl, count - fl, user); + + rt->rt_write += count; + rt->rt_write %= rt->buffer_size; + + return(count); +} + + +static int file_open(struct inode *inode, struct file *filp) +{ + int minor = MINOR(inode->i_rdev); + + return rtlx_open(minor, (filp->f_flags & O_NONBLOCK) ? 0 : 1); +} + +static int file_release(struct inode *inode, struct file *filp) +{ + int minor; + minor = MINOR(inode->i_rdev); + + return rtlx_release(minor); +} + +static unsigned int file_poll(struct file *file, poll_table * wait) +{ + int minor; + unsigned int mask = 0; + + minor = MINOR(file->f_dentry->d_inode->i_rdev); + + poll_wait(file, &channel_wqs[minor].rt_queue, wait); + poll_wait(file, &channel_wqs[minor].lx_queue, wait); + + if (rtlx == NULL) + return 0; + + /* data available to read? */ + if (rtlx_read_poll(minor, 0)) + mask |= POLLIN | POLLRDNORM; + + /* space to write */ + if (rtlx_write_poll(minor)) + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + +static ssize_t file_read(struct file *file, char __user * buffer, size_t count, + loff_t * ppos) +{ + int minor = MINOR(file->f_dentry->d_inode->i_rdev); + + /* data available? */ + if (!rtlx_read_poll(minor, (file->f_flags & O_NONBLOCK) ? 0 : 1)) { + return 0; // -EAGAIN makes cat whinge + } + + return rtlx_read(minor, buffer, count, 1); +} + +static ssize_t file_write(struct file *file, const char __user * buffer, size_t count, loff_t * ppos) { - unsigned long failed; int minor; struct rtlx_channel *rt; - size_t fl; DECLARE_WAITQUEUE(wait, current); minor = MINOR(file->f_dentry->d_inode->i_rdev); rt = &rtlx->channel[minor]; /* any space left... */ - if (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size)) { + if (!rtlx_write_poll(minor)) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; @@ -263,61 +467,64 @@ static ssize_t rtlx_write(struct file *file, const char __user * buffer, add_wait_queue(&channel_wqs[minor].rt_queue, &wait); set_current_state(TASK_INTERRUPTIBLE); - while (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size)) + while (!rtlx_write_poll(minor)) schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&channel_wqs[minor].rt_queue, &wait); } - /* total number of bytes to copy */ - count = min(count, (size_t)spacefree(rt->rt_read, rt->rt_write, rt->buffer_size) ); - - /* first bit from write pointer to the end of the buffer, or count */ - fl = min(count, (size_t) rt->buffer_size - rt->rt_write); - - failed = copy_from_user(&rt->rt_buffer[rt->rt_write], buffer, fl); - if (failed) { - count = fl - failed; - goto out; - } - - /* if there's any left copy to the beginning of the buffer */ - if (count - fl) { - failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl); - if (failed) { - count -= failed; - goto out; - } - } - -out: - rt->rt_write += count; - rt->rt_write %= rt->buffer_size; - - return count; + return rtlx_write(minor, (void *)buffer, count, 1); } static struct file_operations rtlx_fops = { - .owner = THIS_MODULE, - .open = rtlx_open, - .release = rtlx_release, - .write = rtlx_write, - .read = rtlx_read, - .poll = rtlx_poll + .owner = THIS_MODULE, + .open = file_open, + .release = file_release, + .write = file_write, + .read = file_read, + .poll = file_poll }; +static struct irqaction rtlx_irq = { + .handler = rtlx_interrupt, + .flags = SA_INTERRUPT, + .name = "RTLX", +}; + +static int rtlx_irq_num = MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ; + static char register_chrdev_failed[] __initdata = KERN_ERR "rtlx_module_init: unable to register device\n"; -static int __init rtlx_module_init(void) +static int rtlx_module_init(void) { + int i; + major = register_chrdev(0, module_name, &rtlx_fops); if (major < 0) { printk(register_chrdev_failed); return major; } + /* initialise the wait queues */ + for (i = 0; i < RTLX_CHANNELS; i++) { + init_waitqueue_head(&channel_wqs[i].rt_queue); + init_waitqueue_head(&channel_wqs[i].lx_queue); + channel_wqs[i].in_open = 0; + } + + /* set up notifiers */ + notify.start = starting; + notify.stop = stopping; + vpe_notify(RTLX_TARG_VPE, ¬ify); + + if (cpu_has_vint) + set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch); + + rtlx_irq.dev_id = rtlx; + setup_irq(rtlx_irq_num, &rtlx_irq); + return 0; } @@ -330,5 +537,5 @@ module_init(rtlx_module_init); module_exit(rtlx_module_exit); MODULE_DESCRIPTION("MIPS RTLX"); -MODULE_AUTHOR("Elizabeth Clarke, MIPS Technologies, Inc."); +MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc."); MODULE_LICENSE("GPL"); diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c index ae83b755cf4a..80ffaa6d50ad 100644 --- a/arch/mips/kernel/vpe.c +++ b/arch/mips/kernel/vpe.c @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * */ /* @@ -27,11 +26,8 @@ * * To load and run, simply cat a SP 'program file' to /dev/vpe1. * i.e cat spapp >/dev/vpe1. - * - * You'll need to have the following device files. - * mknod /dev/vpe0 c 63 0 - * mknod /dev/vpe1 c 63 1 */ + #include #include #include @@ -55,6 +51,8 @@ #include #include #include +#include +#include typedef void *vpe_handle; @@ -68,6 +66,11 @@ typedef void *vpe_handle; static char module_name[] = "vpe"; static int major; +#ifdef CONFIG_MIPS_APSP_KSPD + static struct kspd_notifications kspd_events; +static int kspd_events_reqd = 0; +#endif + /* grab the likely amount of memory we will need. */ #ifdef CONFIG_MIPS_VPE_LOADER_TOM #define P_SIZE (2 * 1024 * 1024) @@ -76,7 +79,10 @@ static int major; #define P_SIZE (256 * 1024) #endif +extern unsigned long physical_memsize; + #define MAX_VPES 16 +#define VPE_PATH_MAX 256 enum vpe_state { VPE_STATE_UNUSED = 0, @@ -102,6 +108,8 @@ struct vpe { unsigned long len; char *pbuffer; unsigned long plen; + unsigned int uid, gid; + char cwd[VPE_PATH_MAX]; unsigned long __start; @@ -113,6 +121,9 @@ struct vpe { /* shared symbol address */ void *shared_ptr; + + /* the list of who wants to know when something major happens */ + struct list_head notify; }; struct tc { @@ -138,7 +149,7 @@ struct vpecontrol_ { } vpecontrol; static void release_progmem(void *ptr); -static void dump_vpe(struct vpe * v); +/* static __attribute_used__ void dump_vpe(struct vpe * v); */ extern void save_gp_address(unsigned int secbase, unsigned int rel); /* get the vpe associated with this minor */ @@ -146,12 +157,14 @@ struct vpe *get_vpe(int minor) { struct vpe *v; + if (!cpu_has_mipsmt) + return NULL; + list_for_each_entry(v, &vpecontrol.vpe_list, list) { if (v->minor == minor) return v; } - printk(KERN_DEBUG "VPE: get_vpe minor %d not found\n", minor); return NULL; } @@ -165,8 +178,6 @@ struct tc *get_tc(int index) return t; } - printk(KERN_DEBUG "VPE: get_tc index %d not found\n", index); - return NULL; } @@ -179,8 +190,6 @@ struct tc *get_tc_unused(void) return t; } - printk(KERN_DEBUG "VPE: All TC's are in use\n"); - return NULL; } @@ -190,13 +199,13 @@ struct vpe *alloc_vpe(int minor) struct vpe *v; if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) { - printk(KERN_WARNING "VPE: alloc_vpe no mem\n"); return NULL; } INIT_LIST_HEAD(&v->tc); list_add_tail(&v->list, &vpecontrol.vpe_list); + INIT_LIST_HEAD(&v->notify); v->minor = minor; return v; } @@ -207,7 +216,6 @@ struct tc *alloc_tc(int index) struct tc *t; if ((t = kzalloc(sizeof(struct tc), GFP_KERNEL)) == NULL) { - printk(KERN_WARNING "VPE: alloc_tc no mem\n"); return NULL; } @@ -236,20 +244,16 @@ void dump_mtregs(void) printk("config3 0x%lx MT %ld\n", val, (val & CONFIG3_MT) >> CONFIG3_MT_SHIFT); - val = read_c0_mvpconf0(); - printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val, - (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT, - val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT); - val = read_c0_mvpcontrol(); printk("MVPControl 0x%lx, STLB %ld VPC %ld EVP %ld\n", val, (val & MVPCONTROL_STLB) >> MVPCONTROL_STLB_SHIFT, (val & MVPCONTROL_VPC) >> MVPCONTROL_VPC_SHIFT, (val & MVPCONTROL_EVP)); - val = read_c0_vpeconf0(); - printk("VPEConf0 0x%lx MVP %ld\n", val, - (val & VPECONF0_MVP) >> VPECONF0_MVP_SHIFT); + val = read_c0_mvpconf0(); + printk("mvpconf0 0x%lx, PVPE %ld PTC %ld M %ld\n", val, + (val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT, + val & MVPCONF0_PTC, (val & MVPCONF0_M) >> MVPCONF0_M_SHIFT); } /* Find some VPE program space */ @@ -354,9 +358,9 @@ static int apply_r_mips_gprel16(struct module *me, uint32_t *location, } if( (rel > 32768) || (rel < -32768) ) { - printk(KERN_ERR - "apply_r_mips_gprel16: relative address out of range 0x%x %d\n", - rel, rel); + printk(KERN_DEBUG "VPE loader: apply_r_mips_gprel16: " + "relative address 0x%x out of range of gp register\n", + rel); return -ENOEXEC; } @@ -374,8 +378,8 @@ static int apply_r_mips_pc16(struct module *me, uint32_t *location, rel -= 1; // and one instruction less due to the branch delay slot. if( (rel > 32768) || (rel < -32768) ) { - printk(KERN_ERR - "apply_r_mips_pc16: relative address out of range 0x%x\n", rel); + printk(KERN_DEBUG "VPE loader: " + "apply_r_mips_pc16: relative address out of range 0x%x\n", rel); return -ENOEXEC; } @@ -396,7 +400,8 @@ static int apply_r_mips_26(struct module *me, uint32_t *location, Elf32_Addr v) { if (v % 4) { - printk(KERN_ERR "module %s: dangerous relocation mod4\n", me->name); + printk(KERN_DEBUG "VPE loader: apply_r_mips_26 " + " unaligned relocation\n"); return -ENOEXEC; } @@ -459,12 +464,13 @@ static int apply_r_mips_lo16(struct module *me, uint32_t *location, /* * The value for the HI16 had best be the same. */ - if (v != l->value) { - printk("%d != %d\n", v, l->value); - goto out_danger; + if (v != l->value) { + printk(KERN_DEBUG "VPE loader: " + "apply_r_mips_lo16/hi16: " + "inconsistent value information\n"); + return -ENOEXEC; } - /* * Do the HI16 relocation. Note that we actually don't * need to know anything about the LO16 itself, except @@ -500,11 +506,6 @@ static int apply_r_mips_lo16(struct module *me, uint32_t *location, *location = insnlo; return 0; - -out_danger: - printk(KERN_ERR "module %s: dangerous " "relocation\n", me->name); - - return -ENOEXEC; } static int (*reloc_handlers[]) (struct module *me, uint32_t *location, @@ -518,6 +519,15 @@ static int (*reloc_handlers[]) (struct module *me, uint32_t *location, [R_MIPS_PC16] = apply_r_mips_pc16 }; +static char *rstrs[] = { + [R_MIPS_NONE] = "MIPS_NONE", + [R_MIPS_32] = "MIPS_32", + [R_MIPS_26] = "MIPS_26", + [R_MIPS_HI16] = "MIPS_HI16", + [R_MIPS_LO16] = "MIPS_LO16", + [R_MIPS_GPREL16] = "MIPS_GPREL16", + [R_MIPS_PC16] = "MIPS_PC16" +}; int apply_relocations(Elf32_Shdr *sechdrs, const char *strtab, @@ -552,15 +562,13 @@ int apply_relocations(Elf32_Shdr *sechdrs, res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v); if( res ) { - printk(KERN_DEBUG - "relocation error 0x%x sym refer <%s> value 0x%x " - "type 0x%x r_info 0x%x\n", - (unsigned int)location, strtab + sym->st_name, v, - r_info, ELF32_R_TYPE(r_info)); - } - - if (res) + char *r = rstrs[ELF32_R_TYPE(r_info)]; + printk(KERN_WARNING "VPE loader: .text+0x%x " + "relocation type %s for symbol \"%s\" failed\n", + rel[i].r_offset, r ? r : "UNKNOWN", + strtab + sym->st_name); return res; + } } return 0; @@ -576,7 +584,7 @@ void save_gp_address(unsigned int secbase, unsigned int rel) /* Change all symbols so that sh_value encodes the pointer directly. */ -static int simplify_symbols(Elf_Shdr * sechdrs, +static void simplify_symbols(Elf_Shdr * sechdrs, unsigned int symindex, const char *strtab, const char *secstrings, @@ -585,18 +593,21 @@ static int simplify_symbols(Elf_Shdr * sechdrs, Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr; unsigned long secbase, bssbase = 0; unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym); - int ret = 0, size; + int size; /* find the .bss section for COMMON symbols */ for (i = 0; i < nsecs; i++) { - if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) + if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) { bssbase = sechdrs[i].sh_addr; + break; + } } for (i = 1; i < n; i++) { switch (sym[i].st_shndx) { case SHN_COMMON: - /* Allocate space for the symbol in the .bss section. st_value is currently size. + /* Allocate space for the symbol in the .bss section. + st_value is currently size. We want it to have the address of the symbol. */ size = sym[i].st_value; @@ -614,11 +625,9 @@ static int simplify_symbols(Elf_Shdr * sechdrs, break; case SHN_MIPS_SCOMMON: - - printk(KERN_DEBUG - "simplify_symbols: ignoring SHN_MIPS_SCOMMON symbol <%s> st_shndx %d\n", - strtab + sym[i].st_name, sym[i].st_shndx); - + printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON" + "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name, + sym[i].st_shndx); // .sbss section break; @@ -632,10 +641,7 @@ static int simplify_symbols(Elf_Shdr * sechdrs, sym[i].st_value += secbase; break; } - } - - return ret; } #ifdef DEBUG_ELFLOADER @@ -655,9 +661,26 @@ static void dump_elfsymbols(Elf_Shdr * sechdrs, unsigned int symindex, static void dump_tc(struct tc *t) { - printk(KERN_WARNING "VPE: TC index %d TCStatus 0x%lx halt 0x%lx\n", - t->index, read_tc_c0_tcstatus(), read_tc_c0_tchalt()); - printk(KERN_WARNING "VPE: tcrestart 0x%lx\n", read_tc_c0_tcrestart()); + unsigned long val; + + settc(t->index); + printk(KERN_DEBUG "VPE loader: TC index %d targtc %ld " + "TCStatus 0x%lx halt 0x%lx\n", + t->index, read_c0_vpecontrol() & VPECONTROL_TARGTC, + read_tc_c0_tcstatus(), read_tc_c0_tchalt()); + + printk(KERN_DEBUG " tcrestart 0x%lx\n", read_tc_c0_tcrestart()); + printk(KERN_DEBUG " tcbind 0x%lx\n", read_tc_c0_tcbind()); + + val = read_c0_vpeconf0(); + printk(KERN_DEBUG " VPEConf0 0x%lx MVP %ld\n", val, + (val & VPECONF0_MVP) >> VPECONF0_MVP_SHIFT); + + printk(KERN_DEBUG " c0 status 0x%lx\n", read_vpe_c0_status()); + printk(KERN_DEBUG " c0 cause 0x%lx\n", read_vpe_c0_cause()); + + printk(KERN_DEBUG " c0 badvaddr 0x%lx\n", read_vpe_c0_badvaddr()); + printk(KERN_DEBUG " c0 epc 0x%lx\n", read_vpe_c0_epc()); } static void dump_tclist(void) @@ -672,96 +695,108 @@ static void dump_tclist(void) /* We are prepared so configure and start the VPE... */ int vpe_run(struct vpe * v) { - unsigned long val; + struct vpe_notifications *n; + unsigned long val, dmt_flag; struct tc *t; /* check we are the Master VPE */ val = read_c0_vpeconf0(); if (!(val & VPECONF0_MVP)) { printk(KERN_WARNING - "VPE: only Master VPE's are allowed to configure MT\n"); + "VPE loader: only Master VPE's are allowed to configure MT\n"); return -1; } /* disable MT (using dvpe) */ dvpe(); + if (!list_empty(&v->tc)) { + if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) { + printk(KERN_WARNING "VPE loader: TC %d is already in use.\n", + t->index); + return -ENOEXEC; + } + } else { + printk(KERN_WARNING "VPE loader: No TC's associated with VPE %d\n", + v->minor); + return -ENOEXEC; + } + /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); - if (!list_empty(&v->tc)) { - if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) { - printk(KERN_WARNING "VPE: TC %d is already in use.\n", - t->index); - return -ENOEXEC; - } - } else { - printk(KERN_WARNING "VPE: No TC's associated with VPE %d\n", - v->minor); - return -ENOEXEC; - } - settc(t->index); - val = read_vpe_c0_vpeconf0(); - /* should check it is halted, and not activated */ if ((read_tc_c0_tcstatus() & TCSTATUS_A) || !(read_tc_c0_tchalt() & TCHALT_H)) { - printk(KERN_WARNING "VPE: TC %d is already doing something!\n", + printk(KERN_WARNING "VPE loader: TC %d is already doing something!\n", t->index); - dump_tclist(); return -ENOEXEC; } + /* + * Disable multi-threaded execution whilst we activate, clear the + * halt bit and bound the tc to the other VPE... + */ + dmt_flag = dmt(); + /* Write the address we want it to start running from in the TCPC register. */ write_tc_c0_tcrestart((unsigned long)v->__start); - - /* write the sivc_info address to tccontext */ write_tc_c0_tccontext((unsigned long)0); - - /* Set up the XTC bit in vpeconf0 to point at our tc */ - write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | (t->index << VPECONF0_XTC_SHIFT)); - - /* mark the TC as activated, not interrupt exempt and not dynamically allocatable */ + /* + * Mark the TC as activated, not interrupt exempt and not dynamically + * allocatable + */ val = read_tc_c0_tcstatus(); val = (val & ~(TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A; write_tc_c0_tcstatus(val); write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H); - /* set up VPE1 */ - write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE); // no multiple TC's - write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); // enable this VPE - /* * The sde-kit passes 'memsize' to __start in $a3, so set something - * here... - * Or set $a3 (register 7) to zero and define DFLT_STACK_SIZE and + * here... Or set $a3 to zero and define DFLT_STACK_SIZE and * DFLT_HEAP_SIZE when you compile your program */ + mttgpr(7, physical_memsize); + + + /* set up VPE1 */ + /* + * bind the TC to VPE 1 as late as possible so we only have the final + * VPE registers to set up, and so an EJTAG probe can trigger on it + */ + write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | v->minor); - mttgpr(7, 0); + /* Set up the XTC bit in vpeconf0 to point at our tc */ + write_vpe_c0_vpeconf0( (read_vpe_c0_vpeconf0() & ~(VPECONF0_XTC)) + | (t->index << VPECONF0_XTC_SHIFT)); - /* set config to be the same as vpe0, particularly kseg0 coherency alg */ - write_vpe_c0_config(read_c0_config()); + /* enable this VPE */ + write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); /* clear out any left overs from a previous program */ + write_vpe_c0_status(0); write_vpe_c0_cause(0); /* take system out of configuration state */ clear_c0_mvpcontrol(MVPCONTROL_VPC); - /* clear interrupts enabled IE, ERL, EXL, and KSU from c0 status */ - write_vpe_c0_status(read_vpe_c0_status() & ~(ST0_ERL | ST0_KSU | ST0_IE | ST0_EXL)); + /* now safe to re-enable multi-threading */ + emt(dmt_flag); /* set it running */ evpe(EVPE_ENABLE); + list_for_each_entry(n, &v->notify, list) { + n->start(v->minor); + } + return 0; } -static unsigned long find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs, +static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs, unsigned int symindex, const char *strtab, struct module *mod) { @@ -778,26 +813,28 @@ static unsigned long find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs, } } + if ( (v->__start == 0) || (v->shared_ptr == NULL)) + return -1; + return 0; } /* - * Allocates a VPE with some program code space(the load address), copies - * the contents of the program (p)buffer performing relocatations/etc, - * free's it when finished. -*/ + * Allocates a VPE with some program code space(the load address), copies the + * contents of the program (p)buffer performing relocatations/etc, free's it + * when finished. + */ int vpe_elfload(struct vpe * v) { Elf_Ehdr *hdr; Elf_Shdr *sechdrs; long err = 0; char *secstrings, *strtab = NULL; - unsigned int len, i, symindex = 0, strindex = 0; - + unsigned int len, i, symindex = 0, strindex = 0, relocate = 0; struct module mod; // so we can re-use the relocations code memset(&mod, 0, sizeof(struct module)); - strcpy(mod.name, "VPE dummy prog module"); + strcpy(mod.name, "VPE loader"); hdr = (Elf_Ehdr *) v->pbuffer; len = v->plen; @@ -805,16 +842,22 @@ int vpe_elfload(struct vpe * v) /* Sanity checks against insmoding binaries or wrong arch, weird elf version */ if (memcmp(hdr->e_ident, ELFMAG, 4) != 0 - || hdr->e_type != ET_REL || !elf_check_arch(hdr) + || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC) + || !elf_check_arch(hdr) || hdr->e_shentsize != sizeof(*sechdrs)) { printk(KERN_WARNING - "VPE program, wrong arch or weird elf version\n"); + "VPE loader: program wrong arch or weird elf version\n"); return -ENOEXEC; } + if (hdr->e_type == ET_REL) + relocate = 1; + if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) { - printk(KERN_ERR "VPE program length %u truncated\n", len); + printk(KERN_ERR "VPE loader: program length %u truncated\n", + len); + return -ENOEXEC; } @@ -826,82 +869,126 @@ int vpe_elfload(struct vpe * v) /* And these should exist, but gcc whinges if we don't init them */ symindex = strindex = 0; - for (i = 1; i < hdr->e_shnum; i++) { - - if (sechdrs[i].sh_type != SHT_NOBITS - && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) { - printk(KERN_ERR "VPE program length %u truncated\n", - len); - return -ENOEXEC; - } + if (relocate) { + for (i = 1; i < hdr->e_shnum; i++) { + if (sechdrs[i].sh_type != SHT_NOBITS + && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) { + printk(KERN_ERR "VPE program length %u truncated\n", + len); + return -ENOEXEC; + } - /* Mark all sections sh_addr with their address in the - temporary image. */ - sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset; + /* Mark all sections sh_addr with their address in the + temporary image. */ + sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset; - /* Internal symbols and strings. */ - if (sechdrs[i].sh_type == SHT_SYMTAB) { - symindex = i; - strindex = sechdrs[i].sh_link; - strtab = (char *)hdr + sechdrs[strindex].sh_offset; + /* Internal symbols and strings. */ + if (sechdrs[i].sh_type == SHT_SYMTAB) { + symindex = i; + strindex = sechdrs[i].sh_link; + strtab = (char *)hdr + sechdrs[strindex].sh_offset; + } } + layout_sections(&mod, hdr, sechdrs, secstrings); } - layout_sections(&mod, hdr, sechdrs, secstrings); - v->load_addr = alloc_progmem(mod.core_size); memset(v->load_addr, 0, mod.core_size); - printk("VPE elf_loader: loading to %p\n", v->load_addr); + printk("VPE loader: loading to %p\n", v->load_addr); - for (i = 0; i < hdr->e_shnum; i++) { - void *dest; + if (relocate) { + for (i = 0; i < hdr->e_shnum; i++) { + void *dest; - if (!(sechdrs[i].sh_flags & SHF_ALLOC)) - continue; + if (!(sechdrs[i].sh_flags & SHF_ALLOC)) + continue; - dest = v->load_addr + sechdrs[i].sh_entsize; + dest = v->load_addr + sechdrs[i].sh_entsize; - if (sechdrs[i].sh_type != SHT_NOBITS) - memcpy(dest, (void *)sechdrs[i].sh_addr, - sechdrs[i].sh_size); - /* Update sh_addr to point to copy in image. */ - sechdrs[i].sh_addr = (unsigned long)dest; - } + if (sechdrs[i].sh_type != SHT_NOBITS) + memcpy(dest, (void *)sechdrs[i].sh_addr, + sechdrs[i].sh_size); + /* Update sh_addr to point to copy in image. */ + sechdrs[i].sh_addr = (unsigned long)dest; - /* Fix up syms, so that st_value is a pointer to location. */ - err = - simplify_symbols(sechdrs, symindex, strtab, secstrings, - hdr->e_shnum, &mod); - if (err < 0) { - printk(KERN_WARNING "VPE: unable to simplify symbols\n"); - goto cleanup; - } + printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n", + secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr); + } - /* Now do relocations. */ - for (i = 1; i < hdr->e_shnum; i++) { - const char *strtab = (char *)sechdrs[strindex].sh_addr; - unsigned int info = sechdrs[i].sh_info; - - /* Not a valid relocation section? */ - if (info >= hdr->e_shnum) - continue; - - /* Don't bother with non-allocated sections */ - if (!(sechdrs[info].sh_flags & SHF_ALLOC)) - continue; - - if (sechdrs[i].sh_type == SHT_REL) - err = - apply_relocations(sechdrs, strtab, symindex, i, &mod); - else if (sechdrs[i].sh_type == SHT_RELA) - err = apply_relocate_add(sechdrs, strtab, symindex, i, - &mod); - if (err < 0) { - printk(KERN_WARNING - "vpe_elfload: error in relocations err %ld\n", - err); - goto cleanup; + /* Fix up syms, so that st_value is a pointer to location. */ + simplify_symbols(sechdrs, symindex, strtab, secstrings, + hdr->e_shnum, &mod); + + /* Now do relocations. */ + for (i = 1; i < hdr->e_shnum; i++) { + const char *strtab = (char *)sechdrs[strindex].sh_addr; + unsigned int info = sechdrs[i].sh_info; + + /* Not a valid relocation section? */ + if (info >= hdr->e_shnum) + continue; + + /* Don't bother with non-allocated sections */ + if (!(sechdrs[info].sh_flags & SHF_ALLOC)) + continue; + + if (sechdrs[i].sh_type == SHT_REL) + err = apply_relocations(sechdrs, strtab, symindex, i, + &mod); + else if (sechdrs[i].sh_type == SHT_RELA) + err = apply_relocate_add(sechdrs, strtab, symindex, i, + &mod); + if (err < 0) + return err; + + } + } else { + for (i = 0; i < hdr->e_shnum; i++) { + + /* Internal symbols and strings. */ + if (sechdrs[i].sh_type == SHT_SYMTAB) { + symindex = i; + strindex = sechdrs[i].sh_link; + strtab = (char *)hdr + sechdrs[strindex].sh_offset; + + /* mark the symtab's address for when we try to find the + magic symbols */ + sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset; + } + + /* filter sections we dont want in the final image */ + if (!(sechdrs[i].sh_flags & SHF_ALLOC) || + (sechdrs[i].sh_type == SHT_MIPS_REGINFO)) { + printk( KERN_DEBUG " ignoring section, " + "name %s type %x address 0x%x \n", + secstrings + sechdrs[i].sh_name, + sechdrs[i].sh_type, sechdrs[i].sh_addr); + continue; + } + + if (sechdrs[i].sh_addr < (unsigned int)v->load_addr) { + printk( KERN_WARNING "VPE loader: " + "fully linked image has invalid section, " + "name %s type %x address 0x%x, before load " + "address of 0x%x\n", + secstrings + sechdrs[i].sh_name, + sechdrs[i].sh_type, sechdrs[i].sh_addr, + (unsigned int)v->load_addr); + return -ENOEXEC; + } + + printk(KERN_DEBUG " copying section sh_name %s, sh_addr 0x%x " + "size 0x%x0 from x%p\n", + secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr, + sechdrs[i].sh_size, hdr + sechdrs[i].sh_offset); + + if (sechdrs[i].sh_type != SHT_NOBITS) + memcpy((void *)sechdrs[i].sh_addr, + (char *)hdr + sechdrs[i].sh_offset, + sechdrs[i].sh_size); + else + memset((void *)sechdrs[i].sh_addr, 0, sechdrs[i].sh_size); } } @@ -910,71 +997,104 @@ int vpe_elfload(struct vpe * v) (unsigned long)v->load_addr + v->len); if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) { + if (v->__start == 0) { + printk(KERN_WARNING "VPE loader: program does not contain " + "a __start symbol\n"); + return -ENOEXEC; + } - printk(KERN_WARNING - "VPE: program doesn't contain __start or vpe_shared symbols\n"); - err = -ENOEXEC; + if (v->shared_ptr == NULL) + printk(KERN_WARNING "VPE loader: " + "program does not contain vpe_shared symbol.\n" + " Unable to use AMVP (AP/SP) facilities.\n"); } printk(" elf loaded\n"); - -cleanup: - return err; + return 0; } -static void dump_vpe(struct vpe * v) +__attribute_used__ void dump_vpe(struct vpe * v) { struct tc *t; + settc(v->minor); + printk(KERN_DEBUG "VPEControl 0x%lx\n", read_vpe_c0_vpecontrol()); printk(KERN_DEBUG "VPEConf0 0x%lx\n", read_vpe_c0_vpeconf0()); - list_for_each_entry(t, &vpecontrol.tc_list, list) { + list_for_each_entry(t, &vpecontrol.tc_list, list) dump_tc(t); - } } -/* checks for VPE is unused and gets ready to load program */ +static void cleanup_tc(struct tc *tc) +{ + int tmp; + + /* Put MVPE's into 'configuration state' */ + set_c0_mvpcontrol(MVPCONTROL_VPC); + + settc(tc->index); + tmp = read_tc_c0_tcstatus(); + + /* mark not allocated and not dynamically allocatable */ + tmp &= ~(TCSTATUS_A | TCSTATUS_DA); + tmp |= TCSTATUS_IXMT; /* interrupt exempt */ + write_tc_c0_tcstatus(tmp); + + write_tc_c0_tchalt(TCHALT_H); + + /* bind it to anything other than VPE1 */ + write_tc_c0_tcbind(read_tc_c0_tcbind() & ~TCBIND_CURVPE); // | TCBIND_CURVPE + + clear_c0_mvpcontrol(MVPCONTROL_VPC); +} + +static int getcwd(char *buff, int size) +{ + mm_segment_t old_fs; + int ret; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + ret = sys_getcwd(buff,size); + + set_fs(old_fs); + + return ret; +} + +/* checks VPE is unused and gets ready to load program */ static int vpe_open(struct inode *inode, struct file *filp) { - int minor; + int minor, ret; struct vpe *v; + struct vpe_notifications *not; /* assume only 1 device at the mo. */ if ((minor = MINOR(inode->i_rdev)) != 1) { - printk(KERN_WARNING "VPE: only vpe1 is supported\n"); + printk(KERN_WARNING "VPE loader: only vpe1 is supported\n"); return -ENODEV; } if ((v = get_vpe(minor)) == NULL) { - printk(KERN_WARNING "VPE: unable to get vpe\n"); + printk(KERN_WARNING "VPE loader: unable to get vpe\n"); return -ENODEV; } if (v->state != VPE_STATE_UNUSED) { - unsigned long tmp; - struct tc *t; - - printk(KERN_WARNING "VPE: device %d already in use\n", minor); - dvpe(); - dump_vpe(v); - - printk(KERN_WARNING "VPE: re-initialising %d\n", minor); - - release_progmem(v->load_addr); - t = get_tc(minor); - settc(minor); - tmp = read_tc_c0_tcstatus(); + printk(KERN_DEBUG "VPE loader: tc in use dumping regs\n"); - /* mark not allocated and not dynamically allocatable */ - tmp &= ~(TCSTATUS_A | TCSTATUS_DA); - tmp |= TCSTATUS_IXMT; /* interrupt exempt */ - write_tc_c0_tcstatus(tmp); + dump_tc(get_tc(minor)); - write_tc_c0_tchalt(TCHALT_H); + list_for_each_entry(not, &v->notify, list) { + not->stop(minor); + } + release_progmem(v->load_addr); + cleanup_tc(get_tc(minor)); } // allocate it so when we get write ops we know it's expected. @@ -986,6 +1106,24 @@ static int vpe_open(struct inode *inode, struct file *filp) v->load_addr = NULL; v->len = 0; + v->uid = filp->f_uid; + v->gid = filp->f_gid; + +#ifdef CONFIG_MIPS_APSP_KSPD + /* get kspd to tell us when a syscall_exit happens */ + if (!kspd_events_reqd) { + kspd_notify(&kspd_events); + kspd_events_reqd++; + } +#endif + + v->cwd[0] = 0; + ret = getcwd(v->cwd, VPE_PATH_MAX); + if (ret < 0) + printk(KERN_WARNING "VPE loader: open, getcwd returned %d\n", ret); + + v->shared_ptr = NULL; + v->__start = 0; return 0; } @@ -1006,14 +1144,22 @@ static int vpe_release(struct inode *inode, struct file *filp) if (vpe_elfload(v) >= 0) vpe_run(v); else { - printk(KERN_WARNING "VPE: ELF load failed.\n"); + printk(KERN_WARNING "VPE loader: ELF load failed.\n"); ret = -ENOEXEC; } } else { - printk(KERN_WARNING "VPE: only elf files are supported\n"); + printk(KERN_WARNING "VPE loader: only elf files are supported\n"); ret = -ENOEXEC; } + /* It's good to be able to run the SP and if it chokes have a look at + the /dev/rt?. But if we reset the pointer to the shared struct we + loose what has happened. So perhaps if garbage is sent to the vpe + device, use it as a trigger for the reset. Hopefully a nice + executable will be along shortly. */ + if (ret < 0) + v->shared_ptr = NULL; + // cleanup any temp buffers if (v->pbuffer) vfree(v->pbuffer); @@ -1033,21 +1179,19 @@ static ssize_t vpe_write(struct file *file, const char __user * buffer, return -ENODEV; if (v->pbuffer == NULL) { - printk(KERN_ERR "vpe_write: no pbuffer\n"); + printk(KERN_ERR "VPE loader: no buffer for program\n"); return -ENOMEM; } if ((count + v->len) > v->plen) { printk(KERN_WARNING - "VPE Loader: elf size too big. Perhaps strip uneeded symbols\n"); + "VPE loader: elf size too big. Perhaps strip uneeded symbols\n"); return -ENOMEM; } count -= copy_from_user(v->pbuffer + v->len, buffer, count); - if (!count) { - printk("vpe_write: copy_to_user failed\n"); + if (!count) return -EFAULT; - } v->len += count; return ret; @@ -1149,16 +1293,70 @@ void *vpe_get_shared(int index) { struct vpe *v; - if ((v = get_vpe(index)) == NULL) { - printk(KERN_WARNING "vpe: invalid vpe index %d\n", index); + if ((v = get_vpe(index)) == NULL) return NULL; - } return v->shared_ptr; } EXPORT_SYMBOL(vpe_get_shared); +int vpe_getuid(int index) +{ + struct vpe *v; + + if ((v = get_vpe(index)) == NULL) + return -1; + + return v->uid; +} + +EXPORT_SYMBOL(vpe_getuid); + +int vpe_getgid(int index) +{ + struct vpe *v; + + if ((v = get_vpe(index)) == NULL) + return -1; + + return v->gid; +} + +EXPORT_SYMBOL(vpe_getgid); + +int vpe_notify(int index, struct vpe_notifications *notify) +{ + struct vpe *v; + + if ((v = get_vpe(index)) == NULL) + return -1; + + list_add(¬ify->list, &v->notify); + return 0; +} + +EXPORT_SYMBOL(vpe_notify); + +char *vpe_getcwd(int index) +{ + struct vpe *v; + + if ((v = get_vpe(index)) == NULL) + return NULL; + + return v->cwd; +} + +EXPORT_SYMBOL(vpe_getcwd); + +#ifdef CONFIG_MIPS_APSP_KSPD +static void kspd_sp_exit( int sp_id) +{ + cleanup_tc(get_tc(sp_id)); +} +#endif + static int __init vpe_module_init(void) { struct vpe *v = NULL; @@ -1201,7 +1399,8 @@ static int __init vpe_module_init(void) return -ENODEV; } - list_add(&t->tc, &v->tc); /* add the tc to the list of this vpe's tc's. */ + /* add the tc to the list of this vpe's tc's. */ + list_add(&t->tc, &v->tc); /* deactivate all but vpe0 */ if (i != 0) { @@ -1222,10 +1421,12 @@ static int __init vpe_module_init(void) ~(ST0_IM | ST0_IE | ST0_KSU)) | ST0_CU0); - /* set config to be the same as vpe0, particularly kseg0 coherency alg */ + /* + * Set config to be the same as vpe0, + * particularly kseg0 coherency alg + */ write_vpe_c0_config(read_c0_config()); } - } /* TC's */ @@ -1234,23 +1435,28 @@ static int __init vpe_module_init(void) if (i != 0) { unsigned long tmp; - /* tc 0 will of course be running.... */ - if (i == 0) - t->state = TC_STATE_RUNNING; - settc(i); - /* bind a TC to each VPE, May as well put all excess TC's - on the last VPE */ - if (i >= (((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1)) - write_tc_c0_tcbind(read_tc_c0_tcbind() | - ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)); - else - write_tc_c0_tcbind(read_tc_c0_tcbind() | i); + /* Any TC that is bound to VPE0 gets left as is - in case + we are running SMTC on VPE0. A TC that is bound to any + other VPE gets bound to VPE0, ideally I'd like to make + it homeless but it doesn't appear to let me bind a TC + to a non-existent VPE. Which is perfectly reasonable. + + The (un)bound state is visible to an EJTAG probe so may + notify GDB... + */ + + if (((tmp = read_tc_c0_tcbind()) & TCBIND_CURVPE)) { + /* tc is bound >vpe0 */ + write_tc_c0_tcbind(tmp & ~TCBIND_CURVPE); + + t->pvpe = get_vpe(0); /* set the parent vpe */ + } tmp = read_tc_c0_tcstatus(); - /* mark not allocated and not dynamically allocatable */ + /* mark not activated and not dynamically allocatable */ tmp &= ~(TCSTATUS_A | TCSTATUS_DA); tmp |= TCSTATUS_IXMT; /* interrupt exempt */ write_tc_c0_tcstatus(tmp); @@ -1262,6 +1468,9 @@ static int __init vpe_module_init(void) /* release config state */ clear_c0_mvpcontrol(MVPCONTROL_VPC); +#ifdef CONFIG_MIPS_APSP_KSPD + kspd_events.kspd_sp_exit = kspd_sp_exit; +#endif return 0; } @@ -1281,5 +1490,5 @@ static void __exit vpe_module_exit(void) module_init(vpe_module_init); module_exit(vpe_module_exit); MODULE_DESCRIPTION("MIPS VPE Loader"); -MODULE_AUTHOR("Elizabeth Clarke, MIPS Technologies, Inc"); +MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc."); MODULE_LICENSE("GPL"); diff --git a/include/asm-mips/kspd.h b/include/asm-mips/kspd.h new file mode 100644 index 000000000000..4e9e724c8935 --- /dev/null +++ b/include/asm-mips/kspd.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + */ + +#ifndef _ASM_KSPD_H +#define _ASM_KSPD_H + +struct kspd_notifications { + void (*kspd_sp_exit)(int sp_id); + + struct list_head list; +}; + +#ifdef CONFIG_MIPS_APSP_KSPD +extern void kspd_notify(struct kspd_notifications *notify); +#else +static inline void kspd_notify(struct kspd_notifications *notify) +{ +} +#endif + +#endif diff --git a/include/asm-mips/mipsmtregs.h b/include/asm-mips/mipsmtregs.h index a669c0702c66..a5ac1a62f4f4 100644 --- a/include/asm-mips/mipsmtregs.h +++ b/include/asm-mips/mipsmtregs.h @@ -234,7 +234,7 @@ static inline void __raw_emt(void) __asm__ __volatile__( " .set noreorder \n" " .set mips32r2 \n" - " emt \n" + " .word 0x41600be1 # emt \n" " ehb \n" " .set mips0 \n" " .set reorder"); @@ -364,6 +364,9 @@ do { \ #define read_vpe_c0_ebase() mftc0(15,1) #define write_vpe_c0_ebase(val) mttc0(15, 1, val) #define write_vpe_c0_compare(val) mttc0(11, 0, val) +#define read_vpe_c0_badvaddr() mftc0(8, 0) +#define read_vpe_c0_epc() mftc0(14, 0) +#define write_vpe_c0_epc(val) mttc0(14, 0, val) /* TC */ diff --git a/include/asm-mips/rtlx.h b/include/asm-mips/rtlx.h index 1298c3fdf6c9..76cd51c6be39 100644 --- a/include/asm-mips/rtlx.h +++ b/include/asm-mips/rtlx.h @@ -3,32 +3,46 @@ * */ -#ifndef _RTLX_H -#define _RTLX_H_ +#ifndef __ASM_RTLX_H +#define __ASM_RTLX_H_ #define LX_NODE_BASE 10 #define MIPSCPU_INT_BASE 16 #define MIPS_CPU_RTLX_IRQ 0 -#define RTLX_VERSION 1 +#define RTLX_VERSION 2 #define RTLX_xID 0x12345600 #define RTLX_ID (RTLX_xID | RTLX_VERSION) #define RTLX_CHANNELS 8 -#define RTLX_BUFFER_SIZE 1024 +#define RTLX_CHANNEL_STDIO 0 +#define RTLX_CHANNEL_DBG 1 +#define RTLX_CHANNEL_SYSIO 2 -/* - * lx_state bits - */ -#define RTLX_STATE_OPENED 1UL +extern int rtlx_open(int index, int can_sleep); +extern int rtlx_release(int index); +extern ssize_t rtlx_read(int index, void *buff, size_t count, int user); +extern ssize_t rtlx_write(int index, void *buffer, size_t count, int user); +extern unsigned int rtlx_read_poll(int index, int can_sleep); +extern unsigned int rtlx_write_poll(int index); + +enum rtlx_state { + RTLX_STATE_UNUSED, + RTLX_STATE_INITIALISED, + RTLX_STATE_REMOTE_READY, + RTLX_STATE_OPENED +}; + +#define RTLX_BUFFER_SIZE 1024 /* each channel supports read and write. linux (vpe0) reads lx_buffer and writes rt_buffer SP (vpe1) reads rt_buffer and writes lx_buffer */ struct rtlx_channel { - unsigned long lx_state; + enum rtlx_state rt_state; + enum rtlx_state lx_state; int buffer_size; @@ -38,15 +52,13 @@ struct rtlx_channel { int lx_write, lx_read; char *lx_buffer; - - void *queues; - }; struct rtlx_info { unsigned long id; + enum rtlx_state state; struct rtlx_channel channel[RTLX_CHANNELS]; }; -#endif /* _RTLX_H_ */ +#endif /* __ASM_RTLX_H_ */ diff --git a/include/asm-mips/vpe.h b/include/asm-mips/vpe.h new file mode 100644 index 000000000000..c6e1b961537d --- /dev/null +++ b/include/asm-mips/vpe.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + */ + +#ifndef _ASM_VPE_H +#define _ASM_VPE_H + +struct vpe_notifications { + void (*start)(int vpe); + void (*stop)(int vpe); + + struct list_head list; +}; + + +extern int vpe_notify(int index, struct vpe_notifications *notify); + +extern void *vpe_get_shared(int index); +extern int vpe_getuid(int index); +extern int vpe_getgid(int index); +extern char *vpe_getcwd(int index); + +#endif /* _ASM_VPE_H */ -- cgit v1.2.3 From 41c594ab65fc89573af296d192aa5235d09717ab Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 5 Apr 2006 09:45:45 +0100 Subject: [MIPS] MT: Improved multithreading support. Signed-off-by: Ralf Baechle --- arch/mips/Kconfig | 6 +- arch/mips/kernel/Makefile | 4 +- arch/mips/kernel/asm-offsets.c | 3 + arch/mips/kernel/entry.S | 34 + arch/mips/kernel/gdb-low.S | 24 +- arch/mips/kernel/gdb-stub.c | 61 +- arch/mips/kernel/genex.S | 29 + arch/mips/kernel/head.S | 57 ++ arch/mips/kernel/i8259.c | 4 + arch/mips/kernel/irq-msc01.c | 9 + arch/mips/kernel/irq.c | 13 + arch/mips/kernel/mips-mt.c | 449 +++++++++++ arch/mips/kernel/process.c | 10 +- arch/mips/kernel/ptrace.c | 14 + arch/mips/kernel/ptrace32.c | 14 + arch/mips/kernel/r4k_switch.S | 34 +- arch/mips/kernel/smp-mt.c | 349 ++++++++ arch/mips/kernel/smp.c | 10 + arch/mips/kernel/smp_mt.c | 342 -------- arch/mips/kernel/smtc-asm.S | 130 +++ arch/mips/kernel/smtc-proc.c | 93 +++ arch/mips/kernel/smtc.c | 1322 +++++++++++++++++++++++++++++++ arch/mips/kernel/time.c | 3 +- arch/mips/kernel/traps.c | 124 ++- arch/mips/kernel/vmlinux.lds.S | 2 +- arch/mips/mips-boards/generic/init.c | 1 - arch/mips/mips-boards/generic/time.c | 68 +- arch/mips/mips-boards/malta/Makefile | 1 + arch/mips/mips-boards/malta/malta_int.c | 11 +- arch/mips/mips-boards/malta/malta_smp.c | 128 +++ arch/mips/mips-boards/sim/cmdline.c | 59 -- arch/mips/mips-boards/sim/sim_cmdline.c | 6 +- arch/mips/mips-boards/sim/sim_smp.c | 14 - arch/mips/mm/fault.c | 13 +- arch/mips/mm/tlb-r4k.c | 85 +- arch/mips/mm/tlbex.c | 83 +- include/asm-mips/asmmacro.h | 47 ++ include/asm-mips/cpu-info.h | 10 + include/asm-mips/hazards.h | 2 + include/asm-mips/interrupt.h | 65 +- include/asm-mips/irq.h | 29 + include/asm-mips/mips_mt.h | 15 + include/asm-mips/mipsmtregs.h | 11 +- include/asm-mips/mipsregs.h | 133 ++++ include/asm-mips/mmu_context.h | 112 ++- include/asm-mips/processor.h | 6 + include/asm-mips/ptrace.h | 4 + include/asm-mips/r4kcache.h | 128 +++ include/asm-mips/smtc.h | 55 ++ include/asm-mips/smtc_ipi.h | 118 +++ include/asm-mips/smtc_proc.h | 23 + include/asm-mips/stackframe.h | 187 ++++- 52 files changed, 4049 insertions(+), 505 deletions(-) create mode 100644 arch/mips/kernel/mips-mt.c create mode 100644 arch/mips/kernel/smp-mt.c delete mode 100644 arch/mips/kernel/smp_mt.c create mode 100644 arch/mips/kernel/smtc-asm.S create mode 100644 arch/mips/kernel/smtc-proc.c create mode 100644 arch/mips/kernel/smtc.c create mode 100644 arch/mips/mips-boards/malta/malta_smp.c delete mode 100644 arch/mips/mips-boards/sim/cmdline.c create mode 100644 include/asm-mips/mips_mt.h create mode 100644 include/asm-mips/smtc.h create mode 100644 include/asm-mips/smtc_ipi.h create mode 100644 include/asm-mips/smtc_proc.h (limited to 'include') diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index a7bac0459f99..f9be549645ea 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1447,6 +1447,10 @@ choice prompt "MIPS MT options" depends on MIPS_MT +config MIPS_MT_SMTC + bool "SMTC: Use all TCs on all VPEs for SMP" + select SMP + config MIPS_MT_SMP bool "Use 1 TC on each available VPE for SMP" select SMP @@ -1613,7 +1617,7 @@ source "mm/Kconfig" config SMP bool "Multi-Processing support" - depends on CPU_RM9000 || ((SIBYTE_BCM1x80 || SIBYTE_BCM1x55 || SIBYTE_SB1250 || QEMU) && !SIBYTE_STANDALONE) || SGI_IP27 || MIPS_MT_SMP + depends on CPU_RM9000 || ((SIBYTE_BCM1x80 || SIBYTE_BCM1x55 || SIBYTE_SB1250 || QEMU) && !SIBYTE_STANDALONE) || SGI_IP27 || MIPS_MT_SMP || MIPS_MT_SMTC ---help--- This enables support for systems with more than one CPU. If you have a system with only one CPU, like most personal computers, say N. If diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 9ec01de81c04..34e8a256765c 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -34,7 +34,9 @@ obj-$(CONFIG_CPU_R6000) += r6000_fpu.o r4k_switch.o obj-$(CONFIG_SMP) += smp.o -obj-$(CONFIG_MIPS_MT_SMP) += smp_mt.o +obj-$(CONFIG_MIPS_MT) += mips-mt.o +obj-$(CONFIG_MIPS_MT_SMTC) += smtc.o smtc-asm.o smtc-proc.o +obj-$(CONFIG_MIPS_MT_SMP) += smp-mt.o obj-$(CONFIG_MIPS_APSP_KSPD) += kspd.o obj-$(CONFIG_MIPS_VPE_LOADER) += vpe.o diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c index ca6b03c773be..92b28b674d6f 100644 --- a/arch/mips/kernel/asm-offsets.c +++ b/arch/mips/kernel/asm-offsets.c @@ -69,6 +69,9 @@ void output_ptreg_defines(void) offset("#define PT_BVADDR ", struct pt_regs, cp0_badvaddr); offset("#define PT_STATUS ", struct pt_regs, cp0_status); offset("#define PT_CAUSE ", struct pt_regs, cp0_cause); +#ifdef CONFIG_MIPS_MT_SMTC + offset("#define PT_TCSTATUS ", struct pt_regs, cp0_tcstatus); +#endif /* CONFIG_MIPS_MT_SMTC */ size("#define PT_SIZE ", struct pt_regs); linefeed; } diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S index b1939a486d2c..d101d2fb24ca 100644 --- a/arch/mips/kernel/entry.S +++ b/arch/mips/kernel/entry.S @@ -17,6 +17,9 @@ #include #include #include +#ifdef CONFIG_MIPS_MT_SMTC +#include +#endif #ifdef CONFIG_PREEMPT .macro preempt_stop @@ -75,6 +78,37 @@ FEXPORT(syscall_exit) bnez t0, syscall_exit_work FEXPORT(restore_all) # restore full frame +#ifdef CONFIG_MIPS_MT_SMTC +/* Detect and execute deferred IPI "interrupts" */ + move a0,sp + jal deferred_smtc_ipi +/* Re-arm any temporarily masked interrupts not explicitly "acked" */ + mfc0 v0, CP0_TCSTATUS + ori v1, v0, TCSTATUS_IXMT + mtc0 v1, CP0_TCSTATUS + andi v0, TCSTATUS_IXMT + ehb + mfc0 t0, CP0_TCCONTEXT + DMT 9 # dmt t1 + jal mips_ihb + mfc0 t2, CP0_STATUS + andi t3, t0, 0xff00 + or t2, t2, t3 + mtc0 t2, CP0_STATUS + ehb + andi t1, t1, VPECONTROL_TE + beqz t1, 1f + EMT +1: + mfc0 v1, CP0_TCSTATUS + /* We set IXMT above, XOR should cler it here */ + xori v1, v1, TCSTATUS_IXMT + or v1, v0, v1 + mtc0 v1, CP0_TCSTATUS + ehb + xor t0, t0, t3 + mtc0 t0, CP0_TCCONTEXT +#endif /* CONFIG_MIPS_MT_SMTC */ .set noat RESTORE_TEMP RESTORE_AT diff --git a/arch/mips/kernel/gdb-low.S b/arch/mips/kernel/gdb-low.S index 235ad9f6bd35..10f28fb9f008 100644 --- a/arch/mips/kernel/gdb-low.S +++ b/arch/mips/kernel/gdb-low.S @@ -283,11 +283,33 @@ */ 3: +#ifdef CONFIG_MIPS_MT_SMTC + /* Read-modify write of Status must be atomic */ + mfc0 t2, CP0_TCSTATUS + ori t1, t2, TCSTATUS_IXMT + mtc0 t1, CP0_TCSTATUS + andi t2, t2, TCSTATUS_IXMT + ehb + DMT 9 # dmt t1 + jal mips_ihb + nop +#endif /* CONFIG_MIPS_MT_SMTC */ mfc0 t0, CP0_STATUS ori t0, 0x1f xori t0, 0x1f mtc0 t0, CP0_STATUS - +#ifdef CONFIG_MIPS_MT_SMTC + andi t1, t1, VPECONTROL_TE + beqz t1, 9f + nop + EMT # emt +9: + mfc0 t1, CP0_TCSTATUS + xori t1, t1, TCSTATUS_IXMT + or t1, t1, t2 + mtc0 t1, CP0_TCSTATUS + ehb +#endif /* CONFIG_MIPS_MT_SMTC */ LONG_L v0, GDB_FR_STATUS(sp) LONG_L v1, GDB_FR_EPC(sp) mtc0 v0, CP0_STATUS diff --git a/arch/mips/kernel/gdb-stub.c b/arch/mips/kernel/gdb-stub.c index d4f88e0af24c..6ecbdc1fefd1 100644 --- a/arch/mips/kernel/gdb-stub.c +++ b/arch/mips/kernel/gdb-stub.c @@ -140,6 +140,7 @@ #include #include #include +#include /* * external low-level support routines @@ -669,6 +670,64 @@ static void kgdb_wait(void *arg) local_irq_restore(flags); } +/* + * GDB stub needs to call kgdb_wait on all processor with interrupts + * disabled, so it uses it's own special variant. + */ +static int kgdb_smp_call_kgdb_wait(void) +{ +#ifdef CONFIG_SMP + struct call_data_struct data; + int i, cpus = num_online_cpus() - 1; + int cpu = smp_processor_id(); + + /* + * Can die spectacularly if this CPU isn't yet marked online + */ + BUG_ON(!cpu_online(cpu)); + + if (!cpus) + return 0; + + if (spin_is_locked(&smp_call_lock)) { + /* + * Some other processor is trying to make us do something + * but we're not going to respond... give up + */ + return -1; + } + + /* + * We will continue here, accepting the fact that + * the kernel may deadlock if another CPU attempts + * to call smp_call_function now... + */ + + data.func = kgdb_wait; + data.info = NULL; + atomic_set(&data.started, 0); + data.wait = 0; + + spin_lock(&smp_call_lock); + call_data = &data; + mb(); + + /* Send a message to all other CPUs and wait for them to respond */ + for (i = 0; i < NR_CPUS; i++) + if (cpu_online(i) && i != cpu) + core_send_ipi(i, SMP_CALL_FUNCTION); + + /* Wait for response */ + /* FIXME: lock-up detection, backtrace on lock-up */ + while (atomic_read(&data.started) != cpus) + barrier(); + + call_data = NULL; + spin_unlock(&smp_call_lock); +#endif + + return 0; +} /* * This function does all command processing for interfacing to gdb. It @@ -718,7 +777,7 @@ void handle_exception (struct gdb_regs *regs) /* * force other cpus to enter kgdb */ - smp_call_function(kgdb_wait, NULL, 0, 0); + kgdb_smp_call_kgdb_wait(); /* * If we're in breakpoint() increment the PC diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S index 04418b6568b0..ff7af369f286 100644 --- a/arch/mips/kernel/genex.S +++ b/arch/mips/kernel/genex.S @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -171,6 +172,15 @@ NESTED(except_vec_vi, 0, sp) SAVE_AT .set push .set noreorder +#ifdef CONFIG_MIPS_MT_SMTC + /* + * To keep from blindly blocking *all* interrupts + * during service by SMTC kernel, we also want to + * pass the IM value to be cleared. + */ +EXPORT(except_vec_vi_mori) + ori a0, $0, 0 +#endif /* CONFIG_MIPS_MT_SMTC */ EXPORT(except_vec_vi_lui) lui v0, 0 /* Patched */ j except_vec_vi_handler @@ -187,6 +197,25 @@ EXPORT(except_vec_vi_end) NESTED(except_vec_vi_handler, 0, sp) SAVE_TEMP SAVE_STATIC +#ifdef CONFIG_MIPS_MT_SMTC + /* + * SMTC has an interesting problem that interrupts are level-triggered, + * and the CLI macro will clear EXL, potentially causing a duplicate + * interrupt service invocation. So we need to clear the associated + * IM bit of Status prior to doing CLI, and restore it after the + * service routine has been invoked - we must assume that the + * service routine will have cleared the state, and any active + * level represents a new or otherwised unserviced event... + */ + mfc0 t1, CP0_STATUS + and t0, a0, t1 + mfc0 t2, CP0_TCCONTEXT + or t0, t0, t2 + mtc0 t0, CP0_TCCONTEXT + xor t1, t1, t0 + mtc0 t1, CP0_STATUS + ehb +#endif /* CONFIG_MIPS_MT_SMTC */ CLI move a0, sp jalr v0 diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S index 2e9122a4213a..bdf6f6eff721 100644 --- a/arch/mips/kernel/head.S +++ b/arch/mips/kernel/head.S @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -82,12 +83,33 @@ */ .macro setup_c0_status set clr .set push +#ifdef CONFIG_MIPS_MT_SMTC + /* + * For SMTC, we need to set privilege and disable interrupts only for + * the current TC, using the TCStatus register. + */ + mfc0 t0, CP0_TCSTATUS + /* Fortunately CU 0 is in the same place in both registers */ + /* Set TCU0, TMX, TKSU (for later inversion) and IXMT */ + li t1, ST0_CU0 | 0x08001c00 + or t0, t1 + /* Clear TKSU, leave IXMT */ + xori t0, 0x00001800 + mtc0 t0, CP0_TCSTATUS + ehb + /* We need to leave the global IE bit set, but clear EXL...*/ + mfc0 t0, CP0_STATUS + or t0, ST0_CU0 | ST0_EXL | ST0_ERL | \set | \clr + xor t0, ST0_EXL | ST0_ERL | \clr + mtc0 t0, CP0_STATUS +#else mfc0 t0, CP0_STATUS or t0, ST0_CU0|\set|0x1f|\clr xor t0, 0x1f|\clr mtc0 t0, CP0_STATUS .set noreorder sll zero,3 # ehb +#endif .set pop .endm @@ -134,6 +156,24 @@ NESTED(kernel_entry, 16, sp) # kernel entry point ARC64_TWIDDLE_PC +#ifdef CONFIG_MIPS_MT_SMTC + /* + * In SMTC kernel, "CLI" is thread-specific, in TCStatus. + * We still need to enable interrupts globally in Status, + * and clear EXL/ERL. + * + * TCContext is used to track interrupt levels under + * service in SMTC kernel. Clear for boot TC before + * allowing any interrupts. + */ + mtc0 zero, CP0_TCCONTEXT + + mfc0 t0, CP0_STATUS + ori t0, t0, 0xff1f + xori t0, t0, 0x001e + mtc0 t0, CP0_STATUS +#endif /* CONFIG_MIPS_MT_SMTC */ + PTR_LA t0, __bss_start # clear .bss LONG_S zero, (t0) PTR_LA t1, __bss_stop - LONGSIZE @@ -166,8 +206,25 @@ NESTED(kernel_entry, 16, sp) # kernel entry point * function after setting up the stack and gp registers. */ NESTED(smp_bootstrap, 16, sp) +#ifdef CONFIG_MIPS_MT_SMTC + /* + * Read-modify-writes of Status must be atomic, and this + * is one case where CLI is invoked without EXL being + * necessarily set. The CLI and setup_c0_status will + * in fact be redundant for all but the first TC of + * each VPE being booted. + */ + DMT 10 # dmt t2 /* t0, t1 are used by CLI and setup_c0_status() */ + jal mips_ihb +#endif /* CONFIG_MIPS_MT_SMTC */ setup_c0_status_sec smp_slave_setup +#ifdef CONFIG_MIPS_MT_SMTC + andi t2, t2, VPECONTROL_TE + beqz t2, 2f + EMT # emt +2: +#endif /* CONFIG_MIPS_MT_SMTC */ j start_secondary END(smp_bootstrap) #endif /* CONFIG_SMP */ diff --git a/arch/mips/kernel/i8259.c b/arch/mips/kernel/i8259.c index b974ac9057f6..2125ba5f1d9b 100644 --- a/arch/mips/kernel/i8259.c +++ b/arch/mips/kernel/i8259.c @@ -187,6 +187,10 @@ handle_real_irq: outb(cached_21,0x21); outb(0x60+irq,0x20); /* 'Specific EOI' to master */ } +#ifdef CONFIG_MIPS_MT_SMTC + if (irq_hwmask[irq] & ST0_IM) + set_c0_status(irq_hwmask[irq] & ST0_IM); +#endif /* CONFIG_MIPS_MT_SMTC */ spin_unlock_irqrestore(&i8259A_lock, flags); return; diff --git a/arch/mips/kernel/irq-msc01.c b/arch/mips/kernel/irq-msc01.c index 3f653c7cfbf3..97ebdc754b9e 100644 --- a/arch/mips/kernel/irq-msc01.c +++ b/arch/mips/kernel/irq-msc01.c @@ -76,6 +76,11 @@ static void level_mask_and_ack_msc_irq(unsigned int irq) mask_msc_irq(irq); if (!cpu_has_veic) MSCIC_WRITE(MSC01_IC_EOI, 0); +#ifdef CONFIG_MIPS_MT_SMTC + /* This actually needs to be a call into platform code */ + if (irq_hwmask[irq] & ST0_IM) + set_c0_status(irq_hwmask[irq] & ST0_IM); +#endif /* CONFIG_MIPS_MT_SMTC */ } /* @@ -92,6 +97,10 @@ static void edge_mask_and_ack_msc_irq(unsigned int irq) MSCIC_WRITE(MSC01_IC_SUP+irq*8, r | ~MSC01_IC_SUP_EDGE_BIT); MSCIC_WRITE(MSC01_IC_SUP+irq*8, r); } +#ifdef CONFIG_MIPS_MT_SMTC + if (irq_hwmask[irq] & ST0_IM) + set_c0_status(irq_hwmask[irq] & ST0_IM); +#endif /* CONFIG_MIPS_MT_SMTC */ } /* diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index e0efc4f2f93e..3dce742e716f 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -38,6 +38,15 @@ void ack_bad_irq(unsigned int irq) atomic_t irq_err_count; +#ifdef CONFIG_MIPS_MT_SMTC +/* + * SMTC Kernel needs to manipulate low-level CPU interrupt mask + * in do_IRQ. These are passed in setup_irq_smtc() and stored + * in this table. + */ +unsigned long irq_hwmask[NR_IRQS]; +#endif /* CONFIG_MIPS_MT_SMTC */ + #undef do_IRQ /* @@ -49,6 +58,7 @@ asmlinkage unsigned int do_IRQ(unsigned int irq, struct pt_regs *regs) { irq_enter(); + __DO_IRQ_SMTC_HOOK(); __do_IRQ(irq, regs); irq_exit(); @@ -129,6 +139,9 @@ void __init init_IRQ(void) irq_desc[i].depth = 1; irq_desc[i].handler = &no_irq_type; spin_lock_init(&irq_desc[i].lock); +#ifdef CONFIG_MIPS_MT_SMTC + irq_hwmask[i] = 0; +#endif /* CONFIG_MIPS_MT_SMTC */ } arch_init_irq(); diff --git a/arch/mips/kernel/mips-mt.c b/arch/mips/kernel/mips-mt.c new file mode 100644 index 000000000000..02237a685ec7 --- /dev/null +++ b/arch/mips/kernel/mips-mt.c @@ -0,0 +1,449 @@ +/* + * General MIPS MT support routines, usable in AP/SP, SMVP, or SMTC kernels + * Copyright (C) 2005 Mips Technologies, Inc + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * CPU mask used to set process affinity for MT VPEs/TCs with FPUs + */ + +cpumask_t mt_fpu_cpumask; + +#ifdef CONFIG_MIPS_MT_FPAFF + +#include +#include +#include + +unsigned long mt_fpemul_threshold = 0; + +/* + * Replacement functions for the sys_sched_setaffinity() and + * sys_sched_getaffinity() system calls, so that we can integrate + * FPU affinity with the user's requested processor affinity. + * This code is 98% identical with the sys_sched_setaffinity() + * and sys_sched_getaffinity() system calls, and should be + * updated when kernel/sched.c changes. + */ + +/* + * find_process_by_pid - find a process with a matching PID value. + * used in sys_sched_set/getaffinity() in kernel/sched.c, so + * cloned here. + */ +static inline task_t *find_process_by_pid(pid_t pid) +{ + return pid ? find_task_by_pid(pid) : current; +} + + +/* + * mipsmt_sys_sched_setaffinity - set the cpu affinity of a process + */ +asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, + unsigned long __user *user_mask_ptr) +{ + cpumask_t new_mask; + cpumask_t effective_mask; + int retval; + task_t *p; + + if (len < sizeof(new_mask)) + return -EINVAL; + + if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask))) + return -EFAULT; + + lock_cpu_hotplug(); + read_lock(&tasklist_lock); + + p = find_process_by_pid(pid); + if (!p) { + read_unlock(&tasklist_lock); + unlock_cpu_hotplug(); + return -ESRCH; + } + + /* + * It is not safe to call set_cpus_allowed with the + * tasklist_lock held. We will bump the task_struct's + * usage count and drop tasklist_lock before invoking + * set_cpus_allowed. + */ + get_task_struct(p); + + retval = -EPERM; + if ((current->euid != p->euid) && (current->euid != p->uid) && + !capable(CAP_SYS_NICE)) { + read_unlock(&tasklist_lock); + goto out_unlock; + } + + /* Record new user-specified CPU set for future reference */ + p->thread.user_cpus_allowed = new_mask; + + /* Unlock the task list */ + read_unlock(&tasklist_lock); + + /* Compute new global allowed CPU set if necessary */ + if( (p->thread.mflags & MF_FPUBOUND) + && cpus_intersects(new_mask, mt_fpu_cpumask)) { + cpus_and(effective_mask, new_mask, mt_fpu_cpumask); + retval = set_cpus_allowed(p, effective_mask); + } else { + p->thread.mflags &= ~MF_FPUBOUND; + retval = set_cpus_allowed(p, new_mask); + } + + +out_unlock: + put_task_struct(p); + unlock_cpu_hotplug(); + return retval; +} + +/* + * mipsmt_sys_sched_getaffinity - get the cpu affinity of a process + */ +asmlinkage long mipsmt_sys_sched_getaffinity(pid_t pid, unsigned int len, + unsigned long __user *user_mask_ptr) +{ + unsigned int real_len; + cpumask_t mask; + int retval; + task_t *p; + + real_len = sizeof(mask); + if (len < real_len) + return -EINVAL; + + lock_cpu_hotplug(); + read_lock(&tasklist_lock); + + retval = -ESRCH; + p = find_process_by_pid(pid); + if (!p) + goto out_unlock; + + retval = 0; + + cpus_and(mask, p->thread.user_cpus_allowed, cpu_possible_map); + +out_unlock: + read_unlock(&tasklist_lock); + unlock_cpu_hotplug(); + if (retval) + return retval; + if (copy_to_user(user_mask_ptr, &mask, real_len)) + return -EFAULT; + return real_len; +} + +#endif /* CONFIG_MIPS_MT_FPAFF */ + +/* + * Dump new MIPS MT state for the core. Does not leave TCs halted. + * Takes an argument which taken to be a pre-call MVPControl value. + */ + +void mips_mt_regdump(unsigned long mvpctl) +{ + unsigned long flags; + unsigned long vpflags; + unsigned long mvpconf0; + int nvpe; + int ntc; + int i; + int tc; + unsigned long haltval; + unsigned long tcstatval; +#ifdef CONFIG_MIPS_MT_SMTC + void smtc_soft_dump(void); +#endif /* CONFIG_MIPT_MT_SMTC */ + + local_irq_save(flags); + vpflags = dvpe(); + printk("=== MIPS MT State Dump ===\n"); + printk("-- Global State --\n"); + printk(" MVPControl Passed: %08lx\n", mvpctl); + printk(" MVPControl Read: %08lx\n", vpflags); + printk(" MVPConf0 : %08lx\n", (mvpconf0 = read_c0_mvpconf0())); + nvpe = ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; + ntc = ((mvpconf0 & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT) + 1; + printk("-- per-VPE State --\n"); + for(i = 0; i < nvpe; i++) { + for(tc = 0; tc < ntc; tc++) { + settc(tc); + if((read_tc_c0_tcbind() & TCBIND_CURVPE) == i) { + printk(" VPE %d\n", i); + printk(" VPEControl : %08lx\n", read_vpe_c0_vpecontrol()); + printk(" VPEConf0 : %08lx\n", read_vpe_c0_vpeconf0()); + printk(" VPE%d.Status : %08lx\n", + i, read_vpe_c0_status()); + printk(" VPE%d.EPC : %08lx\n", i, read_vpe_c0_epc()); + printk(" VPE%d.Cause : %08lx\n", i, read_vpe_c0_cause()); + printk(" VPE%d.Config7 : %08lx\n", + i, read_vpe_c0_config7()); + break; /* Next VPE */ + } + } + } + printk("-- per-TC State --\n"); + for(tc = 0; tc < ntc; tc++) { + settc(tc); + if(read_tc_c0_tcbind() == read_c0_tcbind()) { + /* Are we dumping ourself? */ + haltval = 0; /* Then we're not halted, and mustn't be */ + tcstatval = flags; /* And pre-dump TCStatus is flags */ + printk(" TC %d (current TC with VPE EPC above)\n", tc); + } else { + haltval = read_tc_c0_tchalt(); + write_tc_c0_tchalt(1); + tcstatval = read_tc_c0_tcstatus(); + printk(" TC %d\n", tc); + } + printk(" TCStatus : %08lx\n", tcstatval); + printk(" TCBind : %08lx\n", read_tc_c0_tcbind()); + printk(" TCRestart : %08lx\n", read_tc_c0_tcrestart()); + printk(" TCHalt : %08lx\n", haltval); + printk(" TCContext : %08lx\n", read_tc_c0_tccontext()); + if (!haltval) + write_tc_c0_tchalt(0); + } +#ifdef CONFIG_MIPS_MT_SMTC + smtc_soft_dump(); +#endif /* CONFIG_MIPT_MT_SMTC */ + printk("===========================\n"); + evpe(vpflags); + local_irq_restore(flags); +} + +static int mt_opt_norps = 0; +static int mt_opt_rpsctl = -1; +static int mt_opt_nblsu = -1; +static int mt_opt_forceconfig7 = 0; +static int mt_opt_config7 = -1; + +static int __init rps_disable(char *s) +{ + mt_opt_norps = 1; + return 1; +} +__setup("norps", rps_disable); + +static int __init rpsctl_set(char *str) +{ + get_option(&str, &mt_opt_rpsctl); + return 1; +} +__setup("rpsctl=", rpsctl_set); + +static int __init nblsu_set(char *str) +{ + get_option(&str, &mt_opt_nblsu); + return 1; +} +__setup("nblsu=", nblsu_set); + +static int __init config7_set(char *str) +{ + get_option(&str, &mt_opt_config7); + mt_opt_forceconfig7 = 1; + return 1; +} +__setup("config7=", config7_set); + +/* Experimental cache flush control parameters that should go away some day */ +int mt_protiflush = 0; +int mt_protdflush = 0; +int mt_n_iflushes = 1; +int mt_n_dflushes = 1; + +static int __init set_protiflush(char *s) +{ + mt_protiflush = 1; + return 1; +} +__setup("protiflush", set_protiflush); + +static int __init set_protdflush(char *s) +{ + mt_protdflush = 1; + return 1; +} +__setup("protdflush", set_protdflush); + +static int __init niflush(char *s) +{ + get_option(&s, &mt_n_iflushes); + return 1; +} +__setup("niflush=", niflush); + +static int __init ndflush(char *s) +{ + get_option(&s, &mt_n_dflushes); + return 1; +} +__setup("ndflush=", ndflush); +#ifdef CONFIG_MIPS_MT_FPAFF +static int fpaff_threshold = -1; + +static int __init fpaff_thresh(char *str) +{ + get_option(&str, &fpaff_threshold); + return 1; +} + +__setup("fpaff=", fpaff_thresh); +#endif /* CONFIG_MIPS_MT_FPAFF */ + +static unsigned int itc_base = 0; + +static int __init set_itc_base(char *str) +{ + get_option(&str, &itc_base); + return 1; +} + +__setup("itcbase=", set_itc_base); + +void mips_mt_set_cpuoptions(void) +{ + unsigned int oconfig7 = read_c0_config7(); + unsigned int nconfig7 = oconfig7; + + if (mt_opt_norps) { + printk("\"norps\" option deprectated: use \"rpsctl=\"\n"); + } + if (mt_opt_rpsctl >= 0) { + printk("34K return prediction stack override set to %d.\n", + mt_opt_rpsctl); + if (mt_opt_rpsctl) + nconfig7 |= (1 << 2); + else + nconfig7 &= ~(1 << 2); + } + if (mt_opt_nblsu >= 0) { + printk("34K ALU/LSU sync override set to %d.\n", mt_opt_nblsu); + if (mt_opt_nblsu) + nconfig7 |= (1 << 5); + else + nconfig7 &= ~(1 << 5); + } + if (mt_opt_forceconfig7) { + printk("CP0.Config7 forced to 0x%08x.\n", mt_opt_config7); + nconfig7 = mt_opt_config7; + } + if (oconfig7 != nconfig7) { + __asm__ __volatile("sync"); + write_c0_config7(nconfig7); + ehb (); + printk("Config7: 0x%08x\n", read_c0_config7()); + } + + /* Report Cache management debug options */ + if (mt_protiflush) + printk("I-cache flushes single-threaded\n"); + if (mt_protdflush) + printk("D-cache flushes single-threaded\n"); + if (mt_n_iflushes != 1) + printk("I-Cache Flushes Repeated %d times\n", mt_n_iflushes); + if (mt_n_dflushes != 1) + printk("D-Cache Flushes Repeated %d times\n", mt_n_dflushes); + +#ifdef CONFIG_MIPS_MT_FPAFF + /* FPU Use Factor empirically derived from experiments on 34K */ +#define FPUSEFACTOR 333 + + if (fpaff_threshold >= 0) { + mt_fpemul_threshold = fpaff_threshold; + } else { + mt_fpemul_threshold = + (FPUSEFACTOR * (loops_per_jiffy/(500000/HZ))) / HZ; + } + printk("FPU Affinity set after %ld emulations\n", + mt_fpemul_threshold); +#endif /* CONFIG_MIPS_MT_FPAFF */ + + if (itc_base != 0) { + /* + * Configure ITC mapping. This code is very + * specific to the 34K core family, which uses + * a special mode bit ("ITC") in the ErrCtl + * register to enable access to ITC control + * registers via cache "tag" operations. + */ + unsigned long ectlval; + unsigned long itcblkgrn; + + /* ErrCtl register is known as "ecc" to Linux */ + ectlval = read_c0_ecc(); + write_c0_ecc(ectlval | (0x1 << 26)); + ehb(); +#define INDEX_0 (0x80000000) +#define INDEX_8 (0x80000008) + /* Read "cache tag" for Dcache pseudo-index 8 */ + cache_op(Index_Load_Tag_D, INDEX_8); + ehb(); + itcblkgrn = read_c0_dtaglo(); + itcblkgrn &= 0xfffe0000; + /* Set for 128 byte pitch of ITC cells */ + itcblkgrn |= 0x00000c00; + /* Stage in Tag register */ + write_c0_dtaglo(itcblkgrn); + ehb(); + /* Write out to ITU with CACHE op */ + cache_op(Index_Store_Tag_D, INDEX_8); + /* Now set base address, and turn ITC on with 0x1 bit */ + write_c0_dtaglo((itc_base & 0xfffffc00) | 0x1 ); + ehb(); + /* Write out to ITU with CACHE op */ + cache_op(Index_Store_Tag_D, INDEX_0); + write_c0_ecc(ectlval); + ehb(); + printk("Mapped %ld ITC cells starting at 0x%08x\n", + ((itcblkgrn & 0x7fe00000) >> 20), itc_base); + } +} + +/* + * Function to protect cache flushes from concurrent execution + * depends on MP software model chosen. + */ + +void mt_cflush_lockdown(void) +{ +#ifdef CONFIG_MIPS_MT_SMTC + void smtc_cflush_lockdown(void); + + smtc_cflush_lockdown(); +#endif /* CONFIG_MIPS_MT_SMTC */ + /* FILL IN VSMP and AP/SP VERSIONS HERE */ +} + +void mt_cflush_release(void) +{ +#ifdef CONFIG_MIPS_MT_SMTC + void smtc_cflush_release(void); + + smtc_cflush_release(); +#endif /* CONFIG_MIPS_MT_SMTC */ + /* FILL IN VSMP and AP/SP VERSIONS HERE */ +} diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index c66db5e5ab62..8b393df460a2 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -41,6 +41,10 @@ #include #include #include +#ifdef CONFIG_MIPS_MT_SMTC +#include +extern void smtc_idle_loop_hook(void); +#endif /* CONFIG_MIPS_MT_SMTC */ /* * The idle thread. There's no useful work to be done, so just try to conserve @@ -51,9 +55,13 @@ ATTRIB_NORET void cpu_idle(void) { /* endless idle loop with no priority at all */ while (1) { - while (!need_resched()) + while (!need_resched()) { +#ifdef CONFIG_MIPS_MT_SMTC + smtc_idle_loop_hook(); +#endif /* CONFIG_MIPS_MT_SMTC */ if (cpu_wait) (*cpu_wait)(); + } preempt_enable_no_resched(); schedule(); preempt_disable(); diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index f838b36cc765..f3106d0771b0 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -248,10 +248,20 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; case FPC_EIR: { /* implementation / version register */ unsigned int flags; +#ifdef CONFIG_MIPS_MT_SMTC + unsigned int irqflags; + unsigned int mtflags; +#endif /* CONFIG_MIPS_MT_SMTC */ if (!cpu_has_fpu) break; +#ifdef CONFIG_MIPS_MT_SMTC + /* Read-modify-write of Status must be atomic */ + local_irq_save(irqflags); + mtflags = dmt(); +#endif /* CONFIG_MIPS_MT_SMTC */ + preempt_disable(); if (cpu_has_mipsmt) { unsigned int vpflags = dvpe(); @@ -266,6 +276,10 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp)); write_c0_status(flags); } +#ifdef CONFIG_MIPS_MT_SMTC + emt(mtflags); + local_irq_restore(irqflags); +#endif /* CONFIG_MIPS_MT_SMTC */ preempt_enable(); break; } diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index 0d5cf97af727..8704dc0496ea 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c @@ -173,12 +173,22 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) break; case FPC_EIR: { /* implementation / version register */ unsigned int flags; +#ifdef CONFIG_MIPS_MT_SMTC + unsigned int irqflags; + unsigned int mtflags; +#endif /* CONFIG_MIPS_MT_SMTC */ if (!cpu_has_fpu) { tmp = 0; break; } +#ifdef CONFIG_MIPS_MT_SMTC + /* Read-modify-write of Status must be atomic */ + local_irq_save(irqflags); + mtflags = dmt(); +#endif /* CONFIG_MIPS_MT_SMTC */ + preempt_disable(); if (cpu_has_mipsmt) { unsigned int vpflags = dvpe(); @@ -193,6 +203,10 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp)); write_c0_status(flags); } +#ifdef CONFIG_MIPS_MT_SMTC + emt(mtflags); + local_irq_restore(irqflags); +#endif /* CONFIG_MIPS_MT_SMTC */ preempt_enable(); break; } diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S index d2afbd19a9c8..0b1b54acee9f 100644 --- a/arch/mips/kernel/r4k_switch.S +++ b/arch/mips/kernel/r4k_switch.S @@ -88,7 +88,18 @@ PTR_ADDIU t0, $28, _THREAD_SIZE - 32 set_saved_sp t0, t1, t2 - +#ifdef CONFIG_MIPS_MT_SMTC + /* Read-modify-writes of Status must be atomic on a VPE */ + mfc0 t2, CP0_TCSTATUS + ori t1, t2, TCSTATUS_IXMT + mtc0 t1, CP0_TCSTATUS + andi t2, t2, TCSTATUS_IXMT + ehb + DMT 8 # dmt t0 + move t1,ra + jal mips_ihb + move ra,t1 +#endif /* CONFIG_MIPS_MT_SMTC */ mfc0 t1, CP0_STATUS /* Do we really need this? */ li a3, 0xff01 and t1, a3 @@ -97,6 +108,18 @@ and a2, a3 or a2, t1 mtc0 a2, CP0_STATUS +#ifdef CONFIG_MIPS_MT_SMTC + ehb + andi t0, t0, VPECONTROL_TE + beqz t0, 1f + emt +1: + mfc0 t1, CP0_TCSTATUS + xori t1, t1, TCSTATUS_IXMT + or t1, t1, t2 + mtc0 t1, CP0_TCSTATUS + ehb +#endif /* CONFIG_MIPS_MT_SMTC */ move v0, a0 jr ra END(resume) @@ -131,10 +154,19 @@ LEAF(_restore_fp) #define FPU_DEFAULT 0x00000000 LEAF(_init_fpu) +#ifdef CONFIG_MIPS_MT_SMTC + /* Rather than manipulate per-VPE Status, set per-TC bit in TCStatus */ + mfc0 t0, CP0_TCSTATUS + /* Bit position is the same for Status, TCStatus */ + li t1, ST0_CU1 + or t0, t1 + mtc0 t0, CP0_TCSTATUS +#else /* Normal MIPS CU1 enable */ mfc0 t0, CP0_STATUS li t1, ST0_CU1 or t0, t1 mtc0 t0, CP0_STATUS +#endif /* CONFIG_MIPS_MT_SMTC */ fpu_enable_hazard li t1, FPU_DEFAULT diff --git a/arch/mips/kernel/smp-mt.c b/arch/mips/kernel/smp-mt.c new file mode 100644 index 000000000000..19b8e4b31b79 --- /dev/null +++ b/arch/mips/kernel/smp-mt.c @@ -0,0 +1,349 @@ +/* + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Copyright (C) 2004, 05, 06 MIPS Technologies, Inc. + * Elizabeth Clarke (beth@mips.com) + * Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2006 Ralf Baechle (ralf@linux-mips.org) + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* This is f*cking wrong */ + +#define MIPS_CPU_IPI_RESCHED_IRQ 0 +#define MIPS_CPU_IPI_CALL_IRQ 1 + +static int cpu_ipi_resched_irq, cpu_ipi_call_irq; + +#if 0 +static void dump_mtregisters(int vpe, int tc) +{ + printk("vpe %d tc %d\n", vpe, tc); + + settc(tc); + + printk(" c0 status 0x%lx\n", read_vpe_c0_status()); + printk(" vpecontrol 0x%lx\n", read_vpe_c0_vpecontrol()); + printk(" vpeconf0 0x%lx\n", read_vpe_c0_vpeconf0()); + printk(" tcstatus 0x%lx\n", read_tc_c0_tcstatus()); + printk(" tcrestart 0x%lx\n", read_tc_c0_tcrestart()); + printk(" tcbind 0x%lx\n", read_tc_c0_tcbind()); + printk(" tchalt 0x%lx\n", read_tc_c0_tchalt()); +} +#endif + +void __init sanitize_tlb_entries(void) +{ + int i, tlbsiz; + unsigned long mvpconf0, ncpu; + + if (!cpu_has_mipsmt) + return; + + /* Enable VPC */ + set_c0_mvpcontrol(MVPCONTROL_VPC); + + back_to_back_c0_hazard(); + + /* Disable TLB sharing */ + clear_c0_mvpcontrol(MVPCONTROL_STLB); + + mvpconf0 = read_c0_mvpconf0(); + + printk(KERN_INFO "MVPConf0 0x%lx TLBS %lx PTLBE %ld\n", mvpconf0, + (mvpconf0 & MVPCONF0_TLBS) >> MVPCONF0_TLBS_SHIFT, + (mvpconf0 & MVPCONF0_PTLBE) >> MVPCONF0_PTLBE_SHIFT); + + tlbsiz = (mvpconf0 & MVPCONF0_PTLBE) >> MVPCONF0_PTLBE_SHIFT; + ncpu = ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; + + printk(" tlbsiz %d ncpu %ld\n", tlbsiz, ncpu); + + if (tlbsiz > 0) { + /* share them out across the vpe's */ + tlbsiz /= ncpu; + + printk(KERN_INFO "setting Config1.MMU_size to %d\n", tlbsiz); + + for (i = 0; i < ncpu; i++) { + settc(i); + + if (i == 0) + write_c0_config1((read_c0_config1() & ~(0x3f << 25)) | (tlbsiz << 25)); + else + write_vpe_c0_config1((read_vpe_c0_config1() & ~(0x3f << 25)) | + (tlbsiz << 25)); + } + } + + clear_c0_mvpcontrol(MVPCONTROL_VPC); +} + +static void ipi_resched_dispatch (struct pt_regs *regs) +{ + do_IRQ(MIPSCPU_INT_BASE + MIPS_CPU_IPI_RESCHED_IRQ, regs); +} + +static void ipi_call_dispatch (struct pt_regs *regs) +{ + do_IRQ(MIPSCPU_INT_BASE + MIPS_CPU_IPI_CALL_IRQ, regs); +} + +irqreturn_t ipi_resched_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + return IRQ_HANDLED; +} + +irqreturn_t ipi_call_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + smp_call_function_interrupt(); + + return IRQ_HANDLED; +} + +static struct irqaction irq_resched = { + .handler = ipi_resched_interrupt, + .flags = SA_INTERRUPT, + .name = "IPI_resched" +}; + +static struct irqaction irq_call = { + .handler = ipi_call_interrupt, + .flags = SA_INTERRUPT, + .name = "IPI_call" +}; + +/* + * Common setup before any secondaries are started + * Make sure all CPU's are in a sensible state before we boot any of the + * secondarys + */ +void plat_smp_setup(void) +{ + unsigned long val; + int i, num; + + if (!cpu_has_mipsmt) + return; + + /* disable MT so we can configure */ + dvpe(); + dmt(); + + mips_mt_set_cpuoptions(); + + /* Put MVPE's into 'configuration state' */ + set_c0_mvpcontrol(MVPCONTROL_VPC); + + val = read_c0_mvpconf0(); + + /* we'll always have more TC's than VPE's, so loop setting everything + to a sensible state */ + for (i = 0, num = 0; i <= ((val & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT); i++) { + settc(i); + + /* VPE's */ + if (i <= ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)) { + + /* deactivate all but vpe0 */ + if (i != 0) { + unsigned long tmp = read_vpe_c0_vpeconf0(); + + tmp &= ~VPECONF0_VPA; + + /* master VPE */ + tmp |= VPECONF0_MVP; + write_vpe_c0_vpeconf0(tmp); + + /* Record this as available CPU */ + cpu_set(i, phys_cpu_present_map); + __cpu_number_map[i] = ++num; + __cpu_logical_map[num] = i; + } + + /* disable multi-threading with TC's */ + write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE); + + if (i != 0) { + write_vpe_c0_status((read_c0_status() & ~(ST0_IM | ST0_IE | ST0_KSU)) | ST0_CU0); + + /* set config to be the same as vpe0, particularly kseg0 coherency alg */ + write_vpe_c0_config( read_c0_config()); + + /* make sure there are no software interrupts pending */ + write_vpe_c0_cause(read_vpe_c0_cause() & ~(C_SW1|C_SW0)); + + /* Propagate Config7 */ + write_vpe_c0_config7(read_c0_config7()); + } + + } + + /* TC's */ + + if (i != 0) { + unsigned long tmp; + + /* bind a TC to each VPE, May as well put all excess TC's + on the last VPE */ + if ( i >= (((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)+1) ) + write_tc_c0_tcbind(read_tc_c0_tcbind() | ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) ); + else { + write_tc_c0_tcbind( read_tc_c0_tcbind() | i); + + /* and set XTC */ + write_vpe_c0_vpeconf0( read_vpe_c0_vpeconf0() | (i << VPECONF0_XTC_SHIFT)); + } + + tmp = read_tc_c0_tcstatus(); + + /* mark not allocated and not dynamically allocatable */ + tmp &= ~(TCSTATUS_A | TCSTATUS_DA); + tmp |= TCSTATUS_IXMT; /* interrupt exempt */ + write_tc_c0_tcstatus(tmp); + + write_tc_c0_tchalt(TCHALT_H); + } + } + + /* Release config state */ + clear_c0_mvpcontrol(MVPCONTROL_VPC); + + /* We'll wait until starting the secondaries before starting MVPE */ + + printk(KERN_INFO "Detected %i available secondary CPU(s)\n", num); +} + +void __init plat_prepare_cpus(unsigned int max_cpus) +{ + /* set up ipi interrupts */ + if (cpu_has_vint) { + set_vi_handler (MIPS_CPU_IPI_RESCHED_IRQ, ipi_resched_dispatch); + set_vi_handler (MIPS_CPU_IPI_CALL_IRQ, ipi_call_dispatch); + } + + cpu_ipi_resched_irq = MIPSCPU_INT_BASE + MIPS_CPU_IPI_RESCHED_IRQ; + cpu_ipi_call_irq = MIPSCPU_INT_BASE + MIPS_CPU_IPI_CALL_IRQ; + + setup_irq(cpu_ipi_resched_irq, &irq_resched); + setup_irq(cpu_ipi_call_irq, &irq_call); + + /* need to mark IPI's as IRQ_PER_CPU */ + irq_desc[cpu_ipi_resched_irq].status |= IRQ_PER_CPU; + irq_desc[cpu_ipi_call_irq].status |= IRQ_PER_CPU; +} + +/* + * Setup the PC, SP, and GP of a secondary processor and start it + * running! + * smp_bootstrap is the place to resume from + * __KSTK_TOS(idle) is apparently the stack pointer + * (unsigned long)idle->thread_info the gp + * assumes a 1:1 mapping of TC => VPE + */ +void prom_boot_secondary(int cpu, struct task_struct *idle) +{ + struct thread_info *gp = task_thread_info(idle); + dvpe(); + set_c0_mvpcontrol(MVPCONTROL_VPC); + + settc(cpu); + + /* restart */ + write_tc_c0_tcrestart((unsigned long)&smp_bootstrap); + + /* enable the tc this vpe/cpu will be running */ + write_tc_c0_tcstatus((read_tc_c0_tcstatus() & ~TCSTATUS_IXMT) | TCSTATUS_A); + + write_tc_c0_tchalt(0); + + /* enable the VPE */ + write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); + + /* stack pointer */ + write_tc_gpr_sp( __KSTK_TOS(idle)); + + /* global pointer */ + write_tc_gpr_gp((unsigned long)gp); + + flush_icache_range((unsigned long)gp, + (unsigned long)(gp + sizeof(struct thread_info))); + + /* finally out of configuration and into chaos */ + clear_c0_mvpcontrol(MVPCONTROL_VPC); + + evpe(EVPE_ENABLE); +} + +void prom_init_secondary(void) +{ + write_c0_status((read_c0_status() & ~ST0_IM ) | + (STATUSF_IP0 | STATUSF_IP1 | STATUSF_IP7)); +} + +void prom_smp_finish(void) +{ + write_c0_compare(read_c0_count() + (8* mips_hpt_frequency/HZ)); + + local_irq_enable(); +} + +void prom_cpus_done(void) +{ +} + +void core_send_ipi(int cpu, unsigned int action) +{ + int i; + unsigned long flags; + int vpflags; + + local_irq_save (flags); + + vpflags = dvpe(); /* cant access the other CPU's registers whilst MVPE enabled */ + + switch (action) { + case SMP_CALL_FUNCTION: + i = C_SW1; + break; + + case SMP_RESCHEDULE_YOURSELF: + default: + i = C_SW0; + break; + } + + /* 1:1 mapping of vpe and tc... */ + settc(cpu); + write_vpe_c0_cause(read_vpe_c0_cause() | i); + evpe(vpflags); + + local_irq_restore(flags); +} diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 72a287aa937e..d42f358754ad 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -38,6 +38,10 @@ #include #include +#ifdef CONFIG_MIPS_MT_SMTC +#include +#endif /* CONFIG_MIPS_MT_SMTC */ + cpumask_t phys_cpu_present_map; /* Bitmask of available CPUs */ volatile cpumask_t cpu_callin_map; /* Bitmask of started secondaries */ cpumask_t cpu_online_map; /* Bitmask of currently online CPUs */ @@ -85,6 +89,10 @@ asmlinkage void start_secondary(void) { unsigned int cpu; +#ifdef CONFIG_MIPS_MT_SMTC + /* Only do cpu_probe for first TC of CPU */ + if ((read_c0_tcbind() & TCBIND_CURTC) == 0) +#endif /* CONFIG_MIPS_MT_SMTC */ cpu_probe(); cpu_report(); per_cpu_trap_init(); @@ -179,11 +187,13 @@ int smp_call_function (void (*func) (void *info), void *info, int retry, if (wait) while (atomic_read(&data.finished) != cpus) barrier(); + call_data = NULL; spin_unlock(&smp_call_lock); return 0; } + void smp_call_function_interrupt(void) { void (*func) (void *info) = call_data->func; diff --git a/arch/mips/kernel/smp_mt.c b/arch/mips/kernel/smp_mt.c deleted file mode 100644 index 993b8bf56aaf..000000000000 --- a/arch/mips/kernel/smp_mt.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright (C) 2004, 2005 MIPS Technologies, Inc. All rights reserved. - * - * Elizabeth Clarke (beth@mips.com) - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - */ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MIPS_CPU_IPI_RESCHED_IRQ 0 -#define MIPS_CPU_IPI_CALL_IRQ 1 - -static int cpu_ipi_resched_irq, cpu_ipi_call_irq; - -#if 0 -static void dump_mtregisters(int vpe, int tc) -{ - printk("vpe %d tc %d\n", vpe, tc); - - settc(tc); - - printk(" c0 status 0x%lx\n", read_vpe_c0_status()); - printk(" vpecontrol 0x%lx\n", read_vpe_c0_vpecontrol()); - printk(" vpeconf0 0x%lx\n", read_vpe_c0_vpeconf0()); - printk(" tcstatus 0x%lx\n", read_tc_c0_tcstatus()); - printk(" tcrestart 0x%lx\n", read_tc_c0_tcrestart()); - printk(" tcbind 0x%lx\n", read_tc_c0_tcbind()); - printk(" tchalt 0x%lx\n", read_tc_c0_tchalt()); -} -#endif - -void __init sanitize_tlb_entries(void) -{ - int i, tlbsiz; - unsigned long mvpconf0, ncpu; - - if (!cpu_has_mipsmt) - return; - - set_c0_mvpcontrol(MVPCONTROL_VPC); - - back_to_back_c0_hazard(); - - /* Disable TLB sharing */ - clear_c0_mvpcontrol(MVPCONTROL_STLB); - - mvpconf0 = read_c0_mvpconf0(); - - printk(KERN_INFO "MVPConf0 0x%lx TLBS %lx PTLBE %ld\n", mvpconf0, - (mvpconf0 & MVPCONF0_TLBS) >> MVPCONF0_TLBS_SHIFT, - (mvpconf0 & MVPCONF0_PTLBE) >> MVPCONF0_PTLBE_SHIFT); - - tlbsiz = (mvpconf0 & MVPCONF0_PTLBE) >> MVPCONF0_PTLBE_SHIFT; - ncpu = ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; - - printk(" tlbsiz %d ncpu %ld\n", tlbsiz, ncpu); - - if (tlbsiz > 0) { - /* share them out across the vpe's */ - tlbsiz /= ncpu; - - printk(KERN_INFO "setting Config1.MMU_size to %d\n", tlbsiz); - - for (i = 0; i < ncpu; i++) { - settc(i); - - if (i == 0) - write_c0_config1((read_c0_config1() & ~(0x3f << 25)) | (tlbsiz << 25)); - else - write_vpe_c0_config1((read_vpe_c0_config1() & ~(0x3f << 25)) | - (tlbsiz << 25)); - } - } - - clear_c0_mvpcontrol(MVPCONTROL_VPC); -} - -static void ipi_resched_dispatch (struct pt_regs *regs) -{ - do_IRQ(MIPS_CPU_IPI_RESCHED_IRQ, regs); -} - -static void ipi_call_dispatch (struct pt_regs *regs) -{ - do_IRQ(MIPS_CPU_IPI_CALL_IRQ, regs); -} - -irqreturn_t ipi_resched_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - return IRQ_HANDLED; -} - -irqreturn_t ipi_call_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - smp_call_function_interrupt(); - - return IRQ_HANDLED; -} - -static struct irqaction irq_resched = { - .handler = ipi_resched_interrupt, - .flags = SA_INTERRUPT, - .name = "IPI_resched" -}; - -static struct irqaction irq_call = { - .handler = ipi_call_interrupt, - .flags = SA_INTERRUPT, - .name = "IPI_call" -}; - -/* - * Common setup before any secondaries are started - * Make sure all CPU's are in a sensible state before we boot any of the - * secondarys - */ -void plat_smp_setup(void) -{ - unsigned long val; - int i, num; - - if (!cpu_has_mipsmt) - return; - - /* disable MT so we can configure */ - dvpe(); - dmt(); - - /* Put MVPE's into 'configuration state' */ - set_c0_mvpcontrol(MVPCONTROL_VPC); - - val = read_c0_mvpconf0(); - - /* we'll always have more TC's than VPE's, so loop setting everything - to a sensible state */ - for (i = 0, num = 0; i <= ((val & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT); i++) { - settc(i); - - /* VPE's */ - if (i <= ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)) { - - /* deactivate all but vpe0 */ - if (i != 0) { - unsigned long tmp = read_vpe_c0_vpeconf0(); - - tmp &= ~VPECONF0_VPA; - - /* master VPE */ - tmp |= VPECONF0_MVP; - write_vpe_c0_vpeconf0(tmp); - - /* Record this as available CPU */ - cpu_set(i, phys_cpu_present_map); - __cpu_number_map[i] = ++num; - __cpu_logical_map[num] = i; - } - - /* disable multi-threading with TC's */ - write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE); - - if (i != 0) { - write_vpe_c0_status((read_c0_status() & ~(ST0_IM | ST0_IE | ST0_KSU)) | ST0_CU0); - write_vpe_c0_cause(read_vpe_c0_cause() & ~CAUSEF_IP); - - /* set config to be the same as vpe0, particularly kseg0 coherency alg */ - write_vpe_c0_config( read_c0_config()); - - /* Propagate Config7 */ - write_vpe_c0_config7(read_c0_config7()); - } - - } - - /* TC's */ - - if (i != 0) { - unsigned long tmp; - - /* bind a TC to each VPE, May as well put all excess TC's - on the last VPE */ - if ( i >= (((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)+1) ) - write_tc_c0_tcbind(read_tc_c0_tcbind() | ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) ); - else { - write_tc_c0_tcbind( read_tc_c0_tcbind() | i); - - /* and set XTC */ - write_vpe_c0_vpeconf0( read_vpe_c0_vpeconf0() | (i << VPECONF0_XTC_SHIFT)); - } - - tmp = read_tc_c0_tcstatus(); - - /* mark not allocated and not dynamically allocatable */ - tmp &= ~(TCSTATUS_A | TCSTATUS_DA); - tmp |= TCSTATUS_IXMT; /* interrupt exempt */ - write_tc_c0_tcstatus(tmp); - - write_tc_c0_tchalt(TCHALT_H); - } - } - - /* Release config state */ - clear_c0_mvpcontrol(MVPCONTROL_VPC); - - /* We'll wait until starting the secondaries before starting MVPE */ - - printk(KERN_INFO "Detected %i available secondary CPU(s)\n", num); - - /* set up ipi interrupts */ - if (cpu_has_vint) { - set_vi_handler (MIPS_CPU_IPI_RESCHED_IRQ, ipi_resched_dispatch); - set_vi_handler (MIPS_CPU_IPI_CALL_IRQ, ipi_call_dispatch); - } -} - -void __init plat_prepare_cpus(unsigned int max_cpus) -{ - cpu_ipi_resched_irq = MIPSCPU_INT_BASE + MIPS_CPU_IPI_RESCHED_IRQ; - cpu_ipi_call_irq = MIPSCPU_INT_BASE + MIPS_CPU_IPI_CALL_IRQ; - - setup_irq(cpu_ipi_resched_irq, &irq_resched); - setup_irq(cpu_ipi_call_irq, &irq_call); - - /* need to mark IPI's as IRQ_PER_CPU */ - irq_desc[cpu_ipi_resched_irq].status |= IRQ_PER_CPU; - irq_desc[cpu_ipi_call_irq].status |= IRQ_PER_CPU; -} - -/* - * Setup the PC, SP, and GP of a secondary processor and start it - * running! - * smp_bootstrap is the place to resume from - * __KSTK_TOS(idle) is apparently the stack pointer - * (unsigned long)idle->thread_info the gp - * assumes a 1:1 mapping of TC => VPE - */ -void prom_boot_secondary(int cpu, struct task_struct *idle) -{ - struct thread_info *gp = task_thread_info(idle); - dvpe(); - set_c0_mvpcontrol(MVPCONTROL_VPC); - - settc(cpu); - - /* restart */ - write_tc_c0_tcrestart((unsigned long)&smp_bootstrap); - - /* enable the tc this vpe/cpu will be running */ - write_tc_c0_tcstatus((read_tc_c0_tcstatus() & ~TCSTATUS_IXMT) | TCSTATUS_A); - - write_tc_c0_tchalt(0); - - /* enable the VPE */ - write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); - - /* stack pointer */ - write_tc_gpr_sp( __KSTK_TOS(idle)); - - /* global pointer */ - write_tc_gpr_gp((unsigned long)gp); - - flush_icache_range((unsigned long)gp, (unsigned long)(gp + 1)); - - /* finally out of configuration and into chaos */ - clear_c0_mvpcontrol(MVPCONTROL_VPC); - - evpe(EVPE_ENABLE); -} - -void prom_init_secondary(void) -{ - write_c0_status((read_c0_status() & ~ST0_IM ) | - (STATUSF_IP0 | STATUSF_IP1 | STATUSF_IP7)); -} - -void prom_smp_finish(void) -{ - write_c0_compare(read_c0_count() + (8* mips_hpt_frequency/HZ)); - - local_irq_enable(); -} - -void prom_cpus_done(void) -{ -} - -void core_send_ipi(int cpu, unsigned int action) -{ - int i; - unsigned long flags; - int vpflags; - - local_irq_save (flags); - - vpflags = dvpe(); /* cant access the other CPU's registers whilst MVPE enabled */ - - switch (action) { - case SMP_CALL_FUNCTION: - i = C_SW1; - break; - - case SMP_RESCHEDULE_YOURSELF: - default: - i = C_SW0; - break; - } - - /* 1:1 mapping of vpe and tc... */ - settc(cpu); - write_vpe_c0_cause(read_vpe_c0_cause() | i); - evpe(vpflags); - - local_irq_restore(flags); -} diff --git a/arch/mips/kernel/smtc-asm.S b/arch/mips/kernel/smtc-asm.S new file mode 100644 index 000000000000..c9d65196d917 --- /dev/null +++ b/arch/mips/kernel/smtc-asm.S @@ -0,0 +1,130 @@ +/* + * Assembly Language Functions for MIPS MT SMTC support + */ + +/* + * This file should be built into the kernel only if CONFIG_MIPS_MT_SMTC is set. */ + +#include +#include +#include +#include + +/* + * "Software Interrupt" linkage. + * + * This is invoked when an "Interrupt" is sent from one TC to another, + * where the TC to be interrupted is halted, has it's Restart address + * and Status values saved by the "remote control" thread, then modified + * to cause execution to begin here, in kenel mode. This code then + * disguises the TC state as that of an exception and transfers + * control to the general exception or vectored interrupt handler. + */ + .set noreorder + +/* +The __smtc_ipi_vector would use k0 and k1 as temporaries and +1) Set EXL (this is per-VPE, so this can't be done by proxy!) +2) Restore the K/CU and IXMT bits to the pre "exception" state + (EXL means no interrupts and access to the kernel map). +3) Set EPC to be the saved value of TCRestart. +4) Jump to the exception handler entry point passed by the sender. + +CAN WE PROVE THAT WE WON'T DO THIS IF INTS DISABLED?? +*/ + +/* + * Reviled and slandered vision: Set EXL and restore K/CU/IXMT + * state of pre-halt thread, then save everything and call + * thought some function pointer to imaginary_exception, which + * will parse a register value or memory message queue to + * deliver things like interprocessor interrupts. On return + * from that function, jump to the global ret_from_irq code + * to invoke the scheduler and return as appropriate. + */ + +#define PT_PADSLOT4 (PT_R0-8) +#define PT_PADSLOT5 (PT_R0-4) + + .text + .align 5 +FEXPORT(__smtc_ipi_vector) + .set noat + /* Disable thread scheduling to make Status update atomic */ + DMT 27 # dmt k1 + ehb + /* Set EXL */ + mfc0 k0,CP0_STATUS + ori k0,k0,ST0_EXL + mtc0 k0,CP0_STATUS + ehb + /* Thread scheduling now inhibited by EXL. Restore TE state. */ + andi k1,k1,VPECONTROL_TE + beqz k1,1f + emt +1: + /* + * The IPI sender has put some information on the anticipated + * kernel stack frame. If we were in user mode, this will be + * built above the saved kernel SP. If we were already in the + * kernel, it will be built above the current CPU SP. + * + * Were we in kernel mode, as indicated by CU0? + */ + sll k1,k0,3 + .set noreorder + bltz k1,2f + move k1,sp + .set reorder + /* + * If previously in user mode, set CU0 and use kernel stack. + */ + li k1,ST0_CU0 + or k1,k1,k0 + mtc0 k1,CP0_STATUS + ehb + get_saved_sp + /* Interrupting TC will have pre-set values in slots in the new frame */ +2: subu k1,k1,PT_SIZE + /* Load TCStatus Value */ + lw k0,PT_TCSTATUS(k1) + /* Write it to TCStatus to restore CU/KSU/IXMT state */ + mtc0 k0,$2,1 + ehb + lw k0,PT_EPC(k1) + mtc0 k0,CP0_EPC + /* Save all will redundantly recompute the SP, but use it for now */ + SAVE_ALL + CLI + move a0,sp + /* Function to be invoked passed stack pad slot 5 */ + lw t0,PT_PADSLOT5(sp) + /* Argument from sender passed in stack pad slot 4 */ + lw a1,PT_PADSLOT4(sp) + jalr t0 + nop + j ret_from_irq + nop + +/* + * Called from idle loop to provoke processing of queued IPIs + * First IPI message in queue passed as argument. + */ + +LEAF(self_ipi) + /* Before anything else, block interrupts */ + mfc0 t0,CP0_TCSTATUS + ori t1,t0,TCSTATUS_IXMT + mtc0 t1,CP0_TCSTATUS + ehb + /* We know we're in kernel mode, so prepare stack frame */ + subu t1,sp,PT_SIZE + sw ra,PT_EPC(t1) + sw a0,PT_PADSLOT4(t1) + la t2,ipi_decode + sw t2,PT_PADSLOT5(t1) + /* Save pre-disable value of TCStatus */ + sw t0,PT_TCSTATUS(t1) + j __smtc_ipi_vector + nop +END(self_ipi) diff --git a/arch/mips/kernel/smtc-proc.c b/arch/mips/kernel/smtc-proc.c new file mode 100644 index 000000000000..6f3709996172 --- /dev/null +++ b/arch/mips/kernel/smtc-proc.c @@ -0,0 +1,93 @@ +/* + * /proc hooks for SMTC kernel + * Copyright (C) 2005 Mips Technologies, Inc + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * /proc diagnostic and statistics hooks + */ + +/* + * Statistics gathered + */ +unsigned long selfipis[NR_CPUS]; + +struct smtc_cpu_proc smtc_cpu_stats[NR_CPUS]; + +static struct proc_dir_entry *smtc_stats; + +atomic_t smtc_fpu_recoveries; + +static int proc_read_smtc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int totalen = 0; + int len; + int i; + extern unsigned long ebase; + + len = sprintf(page, "SMTC Status Word: 0x%08x\n", smtc_status); + totalen += len; + page += len; + len = sprintf(page, "Config7: 0x%08x\n", read_c0_config7()); + totalen += len; + page += len; + len = sprintf(page, "EBASE: 0x%08lx\n", ebase); + totalen += len; + page += len; + len = sprintf(page, "Counter Interrupts taken per CPU (TC)\n"); + totalen += len; + page += len; + for (i=0; i < NR_CPUS; i++) { + len = sprintf(page, "%d: %ld\n", i, smtc_cpu_stats[i].timerints); + totalen += len; + page += len; + } + len = sprintf(page, "Self-IPIs by CPU:\n"); + totalen += len; + page += len; + for(i = 0; i < NR_CPUS; i++) { + len = sprintf(page, "%d: %ld\n", i, smtc_cpu_stats[i].selfipis); + totalen += len; + page += len; + } + len = sprintf(page, "%d Recoveries of \"stolen\" FPU\n", + atomic_read(&smtc_fpu_recoveries)); + totalen += len; + page += len; + + return totalen; +} + +void init_smtc_stats(void) +{ + int i; + + for (i=0; i +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This file should be built into the kernel only if CONFIG_MIPS_MT_SMTC is set. + */ + +/* + * MIPSCPU_INT_BASE is identically defined in both + * asm-mips/mips-boards/maltaint.h and asm-mips/mips-boards/simint.h, + * but as yet there's no properly organized include structure that + * will ensure that the right *int.h file will be included for a + * given platform build. + */ + +#define MIPSCPU_INT_BASE 16 + +#define MIPS_CPU_IPI_IRQ 1 + +#define LOCK_MT_PRA() \ + local_irq_save(flags); \ + mtflags = dmt() + +#define UNLOCK_MT_PRA() \ + emt(mtflags); \ + local_irq_restore(flags) + +#define LOCK_CORE_PRA() \ + local_irq_save(flags); \ + mtflags = dvpe() + +#define UNLOCK_CORE_PRA() \ + evpe(mtflags); \ + local_irq_restore(flags) + +/* + * Data structures purely associated with SMTC parallelism + */ + + +/* + * Table for tracking ASIDs whose lifetime is prolonged. + */ + +asiduse smtc_live_asid[MAX_SMTC_TLBS][MAX_SMTC_ASIDS]; + +/* + * Clock interrupt "latch" buffers, per "CPU" + */ + +unsigned int ipi_timer_latch[NR_CPUS]; + +/* + * Number of InterProcessor Interupt (IPI) message buffers to allocate + */ + +#define IPIBUF_PER_CPU 4 + +struct smtc_ipi_q IPIQ[NR_CPUS]; +struct smtc_ipi_q freeIPIq; + + +/* Forward declarations */ + +void ipi_decode(struct pt_regs *, struct smtc_ipi *); +void post_direct_ipi(int cpu, struct smtc_ipi *pipi); +void setup_cross_vpe_interrupts(void); +void init_smtc_stats(void); + +/* Global SMTC Status */ + +unsigned int smtc_status = 0; + +/* Boot command line configuration overrides */ + +static int vpelimit = 0; +static int tclimit = 0; +static int ipibuffers = 0; +static int nostlb = 0; +static int asidmask = 0; +unsigned long smtc_asid_mask = 0xff; + +static int __init maxvpes(char *str) +{ + get_option(&str, &vpelimit); + return 1; +} + +static int __init maxtcs(char *str) +{ + get_option(&str, &tclimit); + return 1; +} + +static int __init ipibufs(char *str) +{ + get_option(&str, &ipibuffers); + return 1; +} + +static int __init stlb_disable(char *s) +{ + nostlb = 1; + return 1; +} + +static int __init asidmask_set(char *str) +{ + get_option(&str, &asidmask); + switch(asidmask) { + case 0x1: + case 0x3: + case 0x7: + case 0xf: + case 0x1f: + case 0x3f: + case 0x7f: + case 0xff: + smtc_asid_mask = (unsigned long)asidmask; + break; + default: + printk("ILLEGAL ASID mask 0x%x from command line\n", asidmask); + } + return 1; +} + +__setup("maxvpes=", maxvpes); +__setup("maxtcs=", maxtcs); +__setup("ipibufs=", ipibufs); +__setup("nostlb", stlb_disable); +__setup("asidmask=", asidmask_set); + +/* Enable additional debug checks before going into CPU idle loop */ +#define SMTC_IDLE_HOOK_DEBUG + +#ifdef SMTC_IDLE_HOOK_DEBUG + +static int hang_trig = 0; + +static int __init hangtrig_enable(char *s) +{ + hang_trig = 1; + return 1; +} + + +__setup("hangtrig", hangtrig_enable); + +#define DEFAULT_BLOCKED_IPI_LIMIT 32 + +static int timerq_limit = DEFAULT_BLOCKED_IPI_LIMIT; + +static int __init tintq(char *str) +{ + get_option(&str, &timerq_limit); + return 1; +} + +__setup("tintq=", tintq); + +int imstuckcount[2][8]; +/* vpemask represents IM/IE bits of per-VPE Status registers, low-to-high */ +int vpemask[2][8] = {{0,1,1,0,0,0,0,1},{0,1,0,0,0,0,0,1}}; +int tcnoprog[NR_CPUS]; +static atomic_t idle_hook_initialized = {0}; +static int clock_hang_reported[NR_CPUS]; + +#endif /* SMTC_IDLE_HOOK_DEBUG */ + +/* Initialize shared TLB - the should probably migrate to smtc_setup_cpus() */ + +void __init sanitize_tlb_entries(void) +{ + printk("Deprecated sanitize_tlb_entries() invoked\n"); +} + + +/* + * Configure shared TLB - VPC configuration bit must be set by caller + */ + +void smtc_configure_tlb(void) +{ + int i,tlbsiz,vpes; + unsigned long mvpconf0; + unsigned long config1val; + + /* Set up ASID preservation table */ + for (vpes=0; vpes> MVPCONF0_PVPE_SHIFT) + 1) > 1) { + /* If we have multiple VPEs, try to share the TLB */ + if ((mvpconf0 & MVPCONF0_TLBS) && !nostlb) { + /* + * If TLB sizing is programmable, shared TLB + * size is the total available complement. + * Otherwise, we have to take the sum of all + * static VPE TLB entries. + */ + if ((tlbsiz = ((mvpconf0 & MVPCONF0_PTLBE) + >> MVPCONF0_PTLBE_SHIFT)) == 0) { + /* + * If there's more than one VPE, there had better + * be more than one TC, because we need one to bind + * to each VPE in turn to be able to read + * its configuration state! + */ + settc(1); + /* Stop the TC from doing anything foolish */ + write_tc_c0_tchalt(TCHALT_H); + mips_ihb(); + /* No need to un-Halt - that happens later anyway */ + for (i=0; i < vpes; i++) { + write_tc_c0_tcbind(i); + /* + * To be 100% sure we're really getting the right + * information, we exit the configuration state + * and do an IHB after each rebinding. + */ + write_c0_mvpcontrol( + read_c0_mvpcontrol() & ~ MVPCONTROL_VPC ); + mips_ihb(); + /* + * Only count if the MMU Type indicated is TLB + */ + if(((read_vpe_c0_config() & MIPS_CONF_MT) >> 7) == 1) { + config1val = read_vpe_c0_config1(); + tlbsiz += ((config1val >> 25) & 0x3f) + 1; + } + + /* Put core back in configuration state */ + write_c0_mvpcontrol( + read_c0_mvpcontrol() | MVPCONTROL_VPC ); + mips_ihb(); + } + } + write_c0_mvpcontrol(read_c0_mvpcontrol() | MVPCONTROL_STLB); + + /* + * Setup kernel data structures to use software total, + * rather than read the per-VPE Config1 value. The values + * for "CPU 0" gets copied to all the other CPUs as part + * of their initialization in smtc_cpu_setup(). + */ + + tlbsiz = tlbsiz & 0x3f; /* MIPS32 limits TLB indices to 64 */ + cpu_data[0].tlbsize = tlbsiz; + smtc_status |= SMTC_TLB_SHARED; + + printk("TLB of %d entry pairs shared by %d VPEs\n", + tlbsiz, vpes); + } else { + printk("WARNING: TLB Not Sharable on SMTC Boot!\n"); + } + } +} + + +/* + * Incrementally build the CPU map out of constituent MIPS MT cores, + * using the specified available VPEs and TCs. Plaform code needs + * to ensure that each MIPS MT core invokes this routine on reset, + * one at a time(!). + * + * This version of the build_cpu_map and prepare_cpus routines assumes + * that *all* TCs of a MIPS MT core will be used for Linux, and that + * they will be spread across *all* available VPEs (to minimise the + * loss of efficiency due to exception service serialization). + * An improved version would pick up configuration information and + * possibly leave some TCs/VPEs as "slave" processors. + * + * Use c0_MVPConf0 to find out how many TCs are available, setting up + * phys_cpu_present_map and the logical/physical mappings. + */ + +int __init mipsmt_build_cpu_map(int start_cpu_slot) +{ + int i, ntcs; + + /* + * The CPU map isn't actually used for anything at this point, + * so it's not clear what else we should do apart from set + * everything up so that "logical" = "physical". + */ + ntcs = ((read_c0_mvpconf0() & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT) + 1; + for (i=start_cpu_slot; i 0) + printk("Limit of %d VPEs set\n", vpelimit); + if (tclimit > 0) + printk("Limit of %d TCs set\n", tclimit); + if (nostlb) { + printk("Shared TLB Use Inhibited - UNSAFE for Multi-VPE Operation\n"); + } + if (asidmask) + printk("ASID mask value override to 0x%x\n", asidmask); + + /* Temporary */ +#ifdef SMTC_IDLE_HOOK_DEBUG + if (hang_trig) + printk("Logic Analyser Trigger on suspected TC hang\n"); +#endif /* SMTC_IDLE_HOOK_DEBUG */ + + /* Put MVPE's into 'configuration state' */ + write_c0_mvpcontrol( read_c0_mvpcontrol() | MVPCONTROL_VPC ); + + val = read_c0_mvpconf0(); + nvpe = ((val & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; + if (vpelimit > 0 && nvpe > vpelimit) + nvpe = vpelimit; + ntc = ((val & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT) + 1; + if (ntc > NR_CPUS) + ntc = NR_CPUS; + if (tclimit > 0 && ntc > tclimit) + ntc = tclimit; + tcpervpe = ntc / nvpe; + slop = ntc % nvpe; /* Residual TCs, < NVPE */ + + /* Set up shared TLB */ + smtc_configure_tlb(); + + for (tc = 0, vpe = 0 ; (vpe < nvpe) && (tc < ntc) ; vpe++) { + /* + * Set the MVP bits. + */ + settc(tc); + write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_MVP); + if (vpe != 0) + printk(", "); + printk("VPE %d: TC", vpe); + for (i = 0; i < tcpervpe; i++) { + /* + * TC 0 is bound to VPE 0 at reset, + * and is presumably executing this + * code. Leave it alone! + */ + if (tc != 0) { + smtc_tc_setup(vpe,tc, cpu); + cpu++; + } + printk(" %d", tc); + tc++; + } + if (slop) { + if (tc != 0) { + smtc_tc_setup(vpe,tc, cpu); + cpu++; + } + printk(" %d", tc); + tc++; + slop--; + } + if (vpe != 0) { + /* + * Clear any stale software interrupts from VPE's Cause + */ + write_vpe_c0_cause(0); + + /* + * Clear ERL/EXL of VPEs other than 0 + * and set restricted interrupt enable/mask. + */ + write_vpe_c0_status((read_vpe_c0_status() + & ~(ST0_BEV | ST0_ERL | ST0_EXL | ST0_IM)) + | (STATUSF_IP0 | STATUSF_IP1 | STATUSF_IP7 + | ST0_IE)); + /* + * set config to be the same as vpe0, + * particularly kseg0 coherency alg + */ + write_vpe_c0_config(read_c0_config()); + /* Clear any pending timer interrupt */ + write_vpe_c0_compare(0); + /* Propagate Config7 */ + write_vpe_c0_config7(read_c0_config7()); + } + /* enable multi-threading within VPE */ + write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() | VPECONTROL_TE); + /* enable the VPE */ + write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); + } + + /* + * Pull any physically present but unused TCs out of circulation. + */ + while (tc < (((val & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT) + 1)) { + cpu_clear(tc, phys_cpu_present_map); + cpu_clear(tc, cpu_present_map); + tc++; + } + + /* release config state */ + write_c0_mvpcontrol( read_c0_mvpcontrol() & ~ MVPCONTROL_VPC ); + + printk("\n"); + + /* Set up coprocessor affinity CPU mask(s) */ + + for (tc = 0; tc < ntc; tc++) { + if(cpu_data[tc].options & MIPS_CPU_FPU) + cpu_set(tc, mt_fpu_cpumask); + } + + /* set up ipi interrupts... */ + + /* If we have multiple VPEs running, set up the cross-VPE interrupt */ + + if (nvpe > 1) + setup_cross_vpe_interrupts(); + + /* Set up queue of free IPI "messages". */ + nipi = NR_CPUS * IPIBUF_PER_CPU; + if (ipibuffers > 0) + nipi = ipibuffers; + + pipi = kmalloc(nipi *sizeof(struct smtc_ipi), GFP_KERNEL); + if (pipi == NULL) + panic("kmalloc of IPI message buffers failed\n"); + else + printk("IPI buffer pool of %d buffers\n", nipi); + for (i = 0; i < nipi; i++) { + smtc_ipi_nq(&freeIPIq, pipi); + pipi++; + } + + /* Arm multithreading and enable other VPEs - but all TCs are Halted */ + emt(EMT_ENABLE); + evpe(EVPE_ENABLE); + local_irq_restore(flags); + /* Initialize SMTC /proc statistics/diagnostics */ + init_smtc_stats(); +} + + +/* + * Setup the PC, SP, and GP of a secondary processor and start it + * running! + * smp_bootstrap is the place to resume from + * __KSTK_TOS(idle) is apparently the stack pointer + * (unsigned long)idle->thread_info the gp + * + */ +void smtc_boot_secondary(int cpu, struct task_struct *idle) +{ + extern u32 kernelsp[NR_CPUS]; + long flags; + int mtflags; + + LOCK_MT_PRA(); + if (cpu_data[cpu].vpe_id != cpu_data[smp_processor_id()].vpe_id) { + dvpe(); + } + settc(cpu_data[cpu].tc_id); + + /* pc */ + write_tc_c0_tcrestart((unsigned long)&smp_bootstrap); + + /* stack pointer */ + kernelsp[cpu] = __KSTK_TOS(idle); + write_tc_gpr_sp(__KSTK_TOS(idle)); + + /* global pointer */ + write_tc_gpr_gp((unsigned long)idle->thread_info); + + smtc_status |= SMTC_MTC_ACTIVE; + write_tc_c0_tchalt(0); + if (cpu_data[cpu].vpe_id != cpu_data[smp_processor_id()].vpe_id) { + evpe(EVPE_ENABLE); + } + UNLOCK_MT_PRA(); +} + +void smtc_init_secondary(void) +{ + /* + * Start timer on secondary VPEs if necessary. + * mips_timer_setup should already have been invoked by init/main + * on "boot" TC. Like per_cpu_trap_init() hack, this assumes that + * SMTC init code assigns TCs consdecutively and in ascending order + * to across available VPEs. + */ + if(((read_c0_tcbind() & TCBIND_CURTC) != 0) + && ((read_c0_tcbind() & TCBIND_CURVPE) + != cpu_data[smp_processor_id() - 1].vpe_id)){ + write_c0_compare (read_c0_count() + mips_hpt_frequency/HZ); + } + + local_irq_enable(); +} + +void smtc_smp_finish(void) +{ + printk("TC %d going on-line as CPU %d\n", + cpu_data[smp_processor_id()].tc_id, smp_processor_id()); +} + +void smtc_cpus_done(void) +{ +} + +/* + * Support for SMTC-optimized driver IRQ registration + */ + +/* + * SMTC Kernel needs to manipulate low-level CPU interrupt mask + * in do_IRQ. These are passed in setup_irq_smtc() and stored + * in this table. + */ + +int setup_irq_smtc(unsigned int irq, struct irqaction * new, + unsigned long hwmask) +{ + irq_hwmask[irq] = hwmask; + + return setup_irq(irq, new); +} + +/* + * IPI model for SMTC is tricky, because interrupts aren't TC-specific. + * Within a VPE one TC can interrupt another by different approaches. + * The easiest to get right would probably be to make all TCs except + * the target IXMT and set a software interrupt, but an IXMT-based + * scheme requires that a handler must run before a new IPI could + * be sent, which would break the "broadcast" loops in MIPS MT. + * A more gonzo approach within a VPE is to halt the TC, extract + * its Restart, Status, and a couple of GPRs, and program the Restart + * address to emulate an interrupt. + * + * Within a VPE, one can be confident that the target TC isn't in + * a critical EXL state when halted, since the write to the Halt + * register could not have issued on the writing thread if the + * halting thread had EXL set. So k0 and k1 of the target TC + * can be used by the injection code. Across VPEs, one can't + * be certain that the target TC isn't in a critical exception + * state. So we try a two-step process of sending a software + * interrupt to the target VPE, which either handles the event + * itself (if it was the target) or injects the event within + * the VPE. + */ + +void smtc_ipi_qdump(void) +{ + int i; + + for (i = 0; i < NR_CPUS ;i++) { + printk("IPIQ[%d]: head = 0x%x, tail = 0x%x, depth = %d\n", + i, (unsigned)IPIQ[i].head, (unsigned)IPIQ[i].tail, + IPIQ[i].depth); + } +} + +/* + * The standard atomic.h primitives don't quite do what we want + * here: We need an atomic add-and-return-previous-value (which + * could be done with atomic_add_return and a decrement) and an + * atomic set/zero-and-return-previous-value (which can't really + * be done with the atomic.h primitives). And since this is + * MIPS MT, we can assume that we have LL/SC. + */ +static __inline__ int atomic_postincrement(unsigned int *pv) +{ + unsigned long result; + + unsigned long temp; + + __asm__ __volatile__( + "1: ll %0, %2 \n" + " addu %1, %0, 1 \n" + " sc %1, %2 \n" + " beqz %1, 1b \n" + " sync \n" + : "=&r" (result), "=&r" (temp), "=m" (*pv) + : "m" (*pv) + : "memory"); + + return result; +} + +/* No longer used in IPI dispatch, but retained for future recycling */ + +static __inline__ int atomic_postclear(unsigned int *pv) +{ + unsigned long result; + + unsigned long temp; + + __asm__ __volatile__( + "1: ll %0, %2 \n" + " or %1, $0, $0 \n" + " sc %1, %2 \n" + " beqz %1, 1b \n" + " sync \n" + : "=&r" (result), "=&r" (temp), "=m" (*pv) + : "m" (*pv) + : "memory"); + + return result; +} + + +void smtc_send_ipi(int cpu, int type, unsigned int action) +{ + int tcstatus; + struct smtc_ipi *pipi; + long flags; + int mtflags; + + if (cpu == smp_processor_id()) { + printk("Cannot Send IPI to self!\n"); + return; + } + /* Set up a descriptor, to be delivered either promptly or queued */ + pipi = smtc_ipi_dq(&freeIPIq); + if (pipi == NULL) { + bust_spinlocks(1); + mips_mt_regdump(dvpe()); + panic("IPI Msg. Buffers Depleted\n"); + } + pipi->type = type; + pipi->arg = (void *)action; + pipi->dest = cpu; + if (cpu_data[cpu].vpe_id != cpu_data[smp_processor_id()].vpe_id) { + /* If not on same VPE, enqueue and send cross-VPE interupt */ + smtc_ipi_nq(&IPIQ[cpu], pipi); + LOCK_CORE_PRA(); + settc(cpu_data[cpu].tc_id); + write_vpe_c0_cause(read_vpe_c0_cause() | C_SW1); + UNLOCK_CORE_PRA(); + } else { + /* + * Not sufficient to do a LOCK_MT_PRA (dmt) here, + * since ASID shootdown on the other VPE may + * collide with this operation. + */ + LOCK_CORE_PRA(); + settc(cpu_data[cpu].tc_id); + /* Halt the targeted TC */ + write_tc_c0_tchalt(TCHALT_H); + mips_ihb(); + + /* + * Inspect TCStatus - if IXMT is set, we have to queue + * a message. Otherwise, we set up the "interrupt" + * of the other TC + */ + tcstatus = read_tc_c0_tcstatus(); + + if ((tcstatus & TCSTATUS_IXMT) != 0) { + /* + * Spin-waiting here can deadlock, + * so we queue the message for the target TC. + */ + write_tc_c0_tchalt(0); + UNLOCK_CORE_PRA(); + /* Try to reduce redundant timer interrupt messages */ + if(type == SMTC_CLOCK_TICK) { + if(atomic_postincrement(&ipi_timer_latch[cpu])!=0) { + smtc_ipi_nq(&freeIPIq, pipi); + return; + } + } + smtc_ipi_nq(&IPIQ[cpu], pipi); + } else { + post_direct_ipi(cpu, pipi); + write_tc_c0_tchalt(0); + UNLOCK_CORE_PRA(); + } + } +} + +/* + * Send IPI message to Halted TC, TargTC/TargVPE already having been set + */ +void post_direct_ipi(int cpu, struct smtc_ipi *pipi) +{ + struct pt_regs *kstack; + unsigned long tcstatus; + unsigned long tcrestart; + extern u32 kernelsp[NR_CPUS]; + extern void __smtc_ipi_vector(void); + + /* Extract Status, EPC from halted TC */ + tcstatus = read_tc_c0_tcstatus(); + tcrestart = read_tc_c0_tcrestart(); + /* If TCRestart indicates a WAIT instruction, advance the PC */ + if ((tcrestart & 0x80000000) + && ((*(unsigned int *)tcrestart & 0xfe00003f) == 0x42000020)) { + tcrestart += 4; + } + /* + * Save on TC's future kernel stack + * + * CU bit of Status is indicator that TC was + * already running on a kernel stack... + */ + if(tcstatus & ST0_CU0) { + /* Note that this "- 1" is pointer arithmetic */ + kstack = ((struct pt_regs *)read_tc_gpr_sp()) - 1; + } else { + kstack = ((struct pt_regs *)kernelsp[cpu]) - 1; + } + + kstack->cp0_epc = (long)tcrestart; + /* Save TCStatus */ + kstack->cp0_tcstatus = tcstatus; + /* Pass token of operation to be performed kernel stack pad area */ + kstack->pad0[4] = (unsigned long)pipi; + /* Pass address of function to be called likewise */ + kstack->pad0[5] = (unsigned long)&ipi_decode; + /* Set interrupt exempt and kernel mode */ + tcstatus |= TCSTATUS_IXMT; + tcstatus &= ~TCSTATUS_TKSU; + write_tc_c0_tcstatus(tcstatus); + ehb(); + /* Set TC Restart address to be SMTC IPI vector */ + write_tc_c0_tcrestart(__smtc_ipi_vector); +} + +void ipi_resched_interrupt(struct pt_regs *regs) +{ + /* Return from interrupt should be enough to cause scheduler check */ +} + + +void ipi_call_interrupt(struct pt_regs *regs) +{ + /* Invoke generic function invocation code in smp.c */ + smp_call_function_interrupt(); +} + +void ipi_decode(struct pt_regs *regs, struct smtc_ipi *pipi) +{ + void *arg_copy = pipi->arg; + int type_copy = pipi->type; + int dest_copy = pipi->dest; + + smtc_ipi_nq(&freeIPIq, pipi); + switch (type_copy) { + case SMTC_CLOCK_TICK: + /* Invoke Clock "Interrupt" */ + ipi_timer_latch[dest_copy] = 0; +#ifdef SMTC_IDLE_HOOK_DEBUG + clock_hang_reported[dest_copy] = 0; +#endif /* SMTC_IDLE_HOOK_DEBUG */ + local_timer_interrupt(0, NULL, regs); + break; + case LINUX_SMP_IPI: + switch ((int)arg_copy) { + case SMP_RESCHEDULE_YOURSELF: + ipi_resched_interrupt(regs); + break; + case SMP_CALL_FUNCTION: + ipi_call_interrupt(regs); + break; + default: + printk("Impossible SMTC IPI Argument 0x%x\n", + (int)arg_copy); + break; + } + break; + default: + printk("Impossible SMTC IPI Type 0x%x\n", type_copy); + break; + } +} + +void deferred_smtc_ipi(struct pt_regs *regs) +{ + struct smtc_ipi *pipi; + unsigned long flags; +/* DEBUG */ + int q = smp_processor_id(); + + /* + * Test is not atomic, but much faster than a dequeue, + * and the vast majority of invocations will have a null queue. + */ + if(IPIQ[q].head != NULL) { + while((pipi = smtc_ipi_dq(&IPIQ[q])) != NULL) { + /* ipi_decode() should be called with interrupts off */ + local_irq_save(flags); + ipi_decode(regs, pipi); + local_irq_restore(flags); + } + } +} + +/* + * Send clock tick to all TCs except the one executing the funtion + */ + +void smtc_timer_broadcast(int vpe) +{ + int cpu; + int myTC = cpu_data[smp_processor_id()].tc_id; + int myVPE = cpu_data[smp_processor_id()].vpe_id; + + smtc_cpu_stats[smp_processor_id()].timerints++; + + for_each_online_cpu(cpu) { + if (cpu_data[cpu].vpe_id == myVPE && + cpu_data[cpu].tc_id != myTC) + smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0); + } +} + +/* + * Cross-VPE interrupts in the SMTC prototype use "software interrupts" + * set via cross-VPE MTTR manipulation of the Cause register. It would be + * in some regards preferable to have external logic for "doorbell" hardware + * interrupts. + */ + +static int cpu_ipi_irq = MIPSCPU_INT_BASE + MIPS_CPU_IPI_IRQ; + +static irqreturn_t ipi_interrupt(int irq, void *dev_idm, struct pt_regs *regs) +{ + int my_vpe = cpu_data[smp_processor_id()].vpe_id; + int my_tc = cpu_data[smp_processor_id()].tc_id; + int cpu; + struct smtc_ipi *pipi; + unsigned long tcstatus; + int sent; + long flags; + unsigned int mtflags; + unsigned int vpflags; + + /* + * So long as cross-VPE interrupts are done via + * MFTR/MTTR read-modify-writes of Cause, we need + * to stop other VPEs whenever the local VPE does + * anything similar. + */ + local_irq_save(flags); + vpflags = dvpe(); + clear_c0_cause(0x100 << MIPS_CPU_IPI_IRQ); + set_c0_status(0x100 << MIPS_CPU_IPI_IRQ); + irq_enable_hazard(); + evpe(vpflags); + local_irq_restore(flags); + + /* + * Cross-VPE Interrupt handler: Try to directly deliver IPIs + * queued for TCs on this VPE other than the current one. + * Return-from-interrupt should cause us to drain the queue + * for the current TC, so we ought not to have to do it explicitly here. + */ + + for_each_online_cpu(cpu) { + if (cpu_data[cpu].vpe_id != my_vpe) + continue; + + pipi = smtc_ipi_dq(&IPIQ[cpu]); + if (pipi != NULL) { + if (cpu_data[cpu].tc_id != my_tc) { + sent = 0; + LOCK_MT_PRA(); + settc(cpu_data[cpu].tc_id); + write_tc_c0_tchalt(TCHALT_H); + mips_ihb(); + tcstatus = read_tc_c0_tcstatus(); + if ((tcstatus & TCSTATUS_IXMT) == 0) { + post_direct_ipi(cpu, pipi); + sent = 1; + } + write_tc_c0_tchalt(0); + UNLOCK_MT_PRA(); + if (!sent) { + smtc_ipi_req(&IPIQ[cpu], pipi); + } + } else { + /* + * ipi_decode() should be called + * with interrupts off + */ + local_irq_save(flags); + ipi_decode(regs, pipi); + local_irq_restore(flags); + } + } + } + + return IRQ_HANDLED; +} + +static void ipi_irq_dispatch(struct pt_regs *regs) +{ + do_IRQ(cpu_ipi_irq, regs); +} + +static struct irqaction irq_ipi; + +void setup_cross_vpe_interrupts(void) +{ + if (!cpu_has_vint) + panic("SMTC Kernel requires Vectored Interupt support"); + + set_vi_handler(MIPS_CPU_IPI_IRQ, ipi_irq_dispatch); + + irq_ipi.handler = ipi_interrupt; + irq_ipi.flags = SA_INTERRUPT; + irq_ipi.name = "SMTC_IPI"; + + setup_irq_smtc(cpu_ipi_irq, &irq_ipi, (0x100 << MIPS_CPU_IPI_IRQ)); + + irq_desc[cpu_ipi_irq].status |= IRQ_PER_CPU; +} + +/* + * SMTC-specific hacks invoked from elsewhere in the kernel. + */ + +void smtc_idle_loop_hook(void) +{ +#ifdef SMTC_IDLE_HOOK_DEBUG + int im; + int flags; + int mtflags; + int bit; + int vpe; + int tc; + int hook_ntcs; + /* + * printk within DMT-protected regions can deadlock, + * so buffer diagnostic messages for later output. + */ + char *pdb_msg; + char id_ho_db_msg[768]; /* worst-case use should be less than 700 */ + + if (atomic_read(&idle_hook_initialized) == 0) { /* fast test */ + if (atomic_add_return(1, &idle_hook_initialized) == 1) { + int mvpconf0; + /* Tedious stuff to just do once */ + mvpconf0 = read_c0_mvpconf0(); + hook_ntcs = ((mvpconf0 & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT) + 1; + if (hook_ntcs > NR_CPUS) + hook_ntcs = NR_CPUS; + for (tc = 0; tc < hook_ntcs; tc++) { + tcnoprog[tc] = 0; + clock_hang_reported[tc] = 0; + } + for (vpe = 0; vpe < 2; vpe++) + for (im = 0; im < 8; im++) + imstuckcount[vpe][im] = 0; + printk("Idle loop test hook initialized for %d TCs\n", hook_ntcs); + atomic_set(&idle_hook_initialized, 1000); + } else { + /* Someone else is initializing in parallel - let 'em finish */ + while (atomic_read(&idle_hook_initialized) < 1000) + ; + } + } + + /* Have we stupidly left IXMT set somewhere? */ + if (read_c0_tcstatus() & 0x400) { + write_c0_tcstatus(read_c0_tcstatus() & ~0x400); + ehb(); + printk("Dangling IXMT in cpu_idle()\n"); + } + + /* Have we stupidly left an IM bit turned off? */ +#define IM_LIMIT 2000 + local_irq_save(flags); + mtflags = dmt(); + pdb_msg = &id_ho_db_msg[0]; + im = read_c0_status(); + vpe = cpu_data[smp_processor_id()].vpe_id; + for (bit = 0; bit < 8; bit++) { + /* + * In current prototype, I/O interrupts + * are masked for VPE > 0 + */ + if (vpemask[vpe][bit]) { + if (!(im & (0x100 << bit))) + imstuckcount[vpe][bit]++; + else + imstuckcount[vpe][bit] = 0; + if (imstuckcount[vpe][bit] > IM_LIMIT) { + set_c0_status(0x100 << bit); + ehb(); + imstuckcount[vpe][bit] = 0; + pdb_msg += sprintf(pdb_msg, + "Dangling IM %d fixed for VPE %d\n", bit, + vpe); + } + } + } + + /* + * Now that we limit outstanding timer IPIs, check for hung TC + */ + for (tc = 0; tc < NR_CPUS; tc++) { + /* Don't check ourself - we'll dequeue IPIs just below */ + if ((tc != smp_processor_id()) && + ipi_timer_latch[tc] > timerq_limit) { + if (clock_hang_reported[tc] == 0) { + pdb_msg += sprintf(pdb_msg, + "TC %d looks hung with timer latch at %d\n", + tc, ipi_timer_latch[tc]); + clock_hang_reported[tc]++; + } + } + } + emt(mtflags); + local_irq_restore(flags); + if (pdb_msg != &id_ho_db_msg[0]) + printk("CPU%d: %s", smp_processor_id(), id_ho_db_msg); +#endif /* SMTC_IDLE_HOOK_DEBUG */ + /* + * To the extent that we've ever turned interrupts off, + * we may have accumulated deferred IPIs. This is subtle. + * If we use the smtc_ipi_qdepth() macro, we'll get an + * exact number - but we'll also disable interrupts + * and create a window of failure where a new IPI gets + * queued after we test the depth but before we re-enable + * interrupts. So long as IXMT never gets set, however, + * we should be OK: If we pick up something and dispatch + * it here, that's great. If we see nothing, but concurrent + * with this operation, another TC sends us an IPI, IXMT + * is clear, and we'll handle it as a real pseudo-interrupt + * and not a pseudo-pseudo interrupt. + */ + if (IPIQ[smp_processor_id()].depth > 0) { + struct smtc_ipi *pipi; + extern void self_ipi(struct smtc_ipi *); + + if ((pipi = smtc_ipi_dq(&IPIQ[smp_processor_id()])) != NULL) { + self_ipi(pipi); + smtc_cpu_stats[smp_processor_id()].selfipis++; + } + } +} + +void smtc_soft_dump(void) +{ + int i; + + printk("Counter Interrupts taken per CPU (TC)\n"); + for (i=0; i < NR_CPUS; i++) { + printk("%d: %ld\n", i, smtc_cpu_stats[i].timerints); + } + printk("Self-IPI invocations:\n"); + for (i=0; i < NR_CPUS; i++) { + printk("%d: %ld\n", i, smtc_cpu_stats[i].selfipis); + } + smtc_ipi_qdump(); + printk("Timer IPI Backlogs:\n"); + for (i=0; i < NR_CPUS; i++) { + printk("%d: %d\n", i, ipi_timer_latch[i]); + } + printk("%d Recoveries of \"stolen\" FPU\n", + atomic_read(&smtc_fpu_recoveries)); +} + + +/* + * TLB management routines special to SMTC + */ + +void smtc_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu) +{ + unsigned long flags, mtflags, tcstat, prevhalt, asid; + int tlb, i; + + /* + * It would be nice to be able to use a spinlock here, + * but this is invoked from within TLB flush routines + * that protect themselves with DVPE, so if a lock is + * held by another TC, it'll never be freed. + * + * DVPE/DMT must not be done with interrupts enabled, + * so even so most callers will already have disabled + * them, let's be really careful... + */ + + local_irq_save(flags); + if (smtc_status & SMTC_TLB_SHARED) { + mtflags = dvpe(); + tlb = 0; + } else { + mtflags = dmt(); + tlb = cpu_data[cpu].vpe_id; + } + asid = asid_cache(cpu); + + do { + if (!((asid += ASID_INC) & ASID_MASK) ) { + if (cpu_has_vtag_icache) + flush_icache_all(); + /* Traverse all online CPUs (hack requires contigous range) */ + for (i = 0; i < num_online_cpus(); i++) { + /* + * We don't need to worry about our own CPU, nor those of + * CPUs who don't share our TLB. + */ + if ((i != smp_processor_id()) && + ((smtc_status & SMTC_TLB_SHARED) || + (cpu_data[i].vpe_id == cpu_data[cpu].vpe_id))) { + settc(cpu_data[i].tc_id); + prevhalt = read_tc_c0_tchalt() & TCHALT_H; + if (!prevhalt) { + write_tc_c0_tchalt(TCHALT_H); + mips_ihb(); + } + tcstat = read_tc_c0_tcstatus(); + smtc_live_asid[tlb][(tcstat & ASID_MASK)] |= (asiduse)(0x1 << i); + if (!prevhalt) + write_tc_c0_tchalt(0); + } + } + if (!asid) /* fix version if needed */ + asid = ASID_FIRST_VERSION; + local_flush_tlb_all(); /* start new asid cycle */ + } + } while (smtc_live_asid[tlb][(asid & ASID_MASK)]); + + /* + * SMTC shares the TLB within VPEs and possibly across all VPEs. + */ + for (i = 0; i < num_online_cpus(); i++) { + if ((smtc_status & SMTC_TLB_SHARED) || + (cpu_data[i].vpe_id == cpu_data[cpu].vpe_id)) + cpu_context(i, mm) = asid_cache(i) = asid; + } + + if (smtc_status & SMTC_TLB_SHARED) + evpe(mtflags); + else + emt(mtflags); + local_irq_restore(flags); +} + +/* + * Invoked from macros defined in mmu_context.h + * which must already have disabled interrupts + * and done a DVPE or DMT as appropriate. + */ + +void smtc_flush_tlb_asid(unsigned long asid) +{ + int entry; + unsigned long ehi; + + entry = read_c0_wired(); + + /* Traverse all non-wired entries */ + while (entry < current_cpu_data.tlbsize) { + write_c0_index(entry); + ehb(); + tlb_read(); + ehb(); + ehi = read_c0_entryhi(); + if((ehi & ASID_MASK) == asid) { + /* + * Invalidate only entries with specified ASID, + * makiing sure all entries differ. + */ + write_c0_entryhi(CKSEG0 + (entry << (PAGE_SHIFT + 1))); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + mtc0_tlbw_hazard(); + tlb_write_indexed(); + } + entry++; + } + write_c0_index(PARKED_INDEX); + tlbw_use_hazard(); +} + +/* + * Support for single-threading cache flush operations. + */ + +int halt_state_save[NR_CPUS]; + +/* + * To really, really be sure that nothing is being done + * by other TCs, halt them all. This code assumes that + * a DVPE has already been done, so while their Halted + * state is theoretically architecturally unstable, in + * practice, it's not going to change while we're looking + * at it. + */ + +void smtc_cflush_lockdown(void) +{ + int cpu; + + for_each_online_cpu(cpu) { + if (cpu != smp_processor_id()) { + settc(cpu_data[cpu].tc_id); + halt_state_save[cpu] = read_tc_c0_tchalt(); + write_tc_c0_tchalt(TCHALT_H); + } + } + mips_ihb(); +} + +/* It would be cheating to change the cpu_online states during a flush! */ + +void smtc_cflush_release(void) +{ + int cpu; + + /* + * Start with a hazard barrier to ensure + * that all CACHE ops have played through. + */ + mips_ihb(); + + for_each_online_cpu(cpu) { + if (cpu != smp_processor_id()) { + settc(cpu_data[cpu].tc_id); + write_tc_c0_tchalt(halt_state_save[cpu]); + } + } + mips_ihb(); +} diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index 5e51a2d8f3f0..13ff4da598cd 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -116,8 +116,7 @@ static void c0_timer_ack(void) write_c0_compare(expirelo); /* Check to see if we have missed any timer interrupts. */ - count = read_c0_count(); - if ((count - expirelo) < 0x7fffffff) { + while (((count = read_c0_count()) - expirelo) < 0x7fffffff) { /* missed_timer_count++; */ expirelo = count + cycles_per_jiffy; write_c0_compare(expirelo); diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 081e6ed5bb62..6336fe8008ec 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -280,9 +280,16 @@ static DEFINE_SPINLOCK(die_lock); NORET_TYPE void ATTRIB_NORET die(const char * str, struct pt_regs * regs) { static int die_counter; +#ifdef CONFIG_MIPS_MT_SMTC + unsigned long dvpret = dvpe(); +#endif /* CONFIG_MIPS_MT_SMTC */ console_verbose(); spin_lock_irq(&die_lock); + bust_spinlocks(1); +#ifdef CONFIG_MIPS_MT_SMTC + mips_mt_regdump(dvpret); +#endif /* CONFIG_MIPS_MT_SMTC */ printk("%s[#%d]:\n", str, ++die_counter); show_registers(regs); spin_unlock_irq(&die_lock); @@ -757,6 +764,7 @@ asmlinkage void do_cpu(struct pt_regs *regs) case 2: case 3: + die_if_kernel("do_cpu invoked from kernel context!", regs); break; } @@ -794,6 +802,36 @@ asmlinkage void do_mcheck(struct pt_regs *regs) asmlinkage void do_mt(struct pt_regs *regs) { + int subcode; + + die_if_kernel("MIPS MT Thread exception in kernel", regs); + + subcode = (read_vpe_c0_vpecontrol() & VPECONTROL_EXCPT) + >> VPECONTROL_EXCPT_SHIFT; + switch (subcode) { + case 0: + printk(KERN_ERR "Thread Underflow\n"); + break; + case 1: + printk(KERN_ERR "Thread Overflow\n"); + break; + case 2: + printk(KERN_ERR "Invalid YIELD Qualifier\n"); + break; + case 3: + printk(KERN_ERR "Gating Storage Exception\n"); + break; + case 4: + printk(KERN_ERR "YIELD Scheduler Exception\n"); + break; + case 5: + printk(KERN_ERR "Gating Storage Schedulier Exception\n"); + break; + default: + printk(KERN_ERR "*** UNKNOWN THREAD EXCEPTION %d ***\n", + subcode); + break; + } die_if_kernel("MIPS MT Thread exception in kernel", regs); force_sig(SIGILL, current); @@ -929,7 +967,15 @@ void ejtag_exception_handler(struct pt_regs *regs) */ void nmi_exception_handler(struct pt_regs *regs) { +#ifdef CONFIG_MIPS_MT_SMTC + unsigned long dvpret = dvpe(); + bust_spinlocks(1); + printk("NMI taken!!!!\n"); + mips_mt_regdump(dvpret); +#else + bust_spinlocks(1); printk("NMI taken!!!!\n"); +#endif /* CONFIG_MIPS_MT_SMTC */ die("NMI", regs); while(1) ; } @@ -1007,7 +1053,7 @@ again: return set; } -void mips_srs_free (int set) +void mips_srs_free(int set) { struct shadow_registers *sr = &shadow_registers; @@ -1027,8 +1073,7 @@ static void *set_vi_srs_handler(int n, void *addr, int srs) if (addr == NULL) { handler = (unsigned long) do_default_vi; srs = 0; - } - else + } else handler = (unsigned long) addr; vi_handlers[n] = (unsigned long) addr; @@ -1040,8 +1085,7 @@ static void *set_vi_srs_handler(int n, void *addr, int srs) if (cpu_has_veic) { if (board_bind_eic_interrupt) board_bind_eic_interrupt (n, srs); - } - else if (cpu_has_vint) { + } else if (cpu_has_vint) { /* SRSMap is only defined if shadow sets are implemented */ if (mips_srs_max() > 1) change_c0_srsmap (0xf << n*4, srs << n*4); @@ -1055,6 +1099,15 @@ static void *set_vi_srs_handler(int n, void *addr, int srs) extern char except_vec_vi, except_vec_vi_lui; extern char except_vec_vi_ori, except_vec_vi_end; +#ifdef CONFIG_MIPS_MT_SMTC + /* + * We need to provide the SMTC vectored interrupt handler + * not only with the address of the handler, but with the + * Status.IM bit to be masked before going there. + */ + extern char except_vec_vi_mori; + const int mori_offset = &except_vec_vi_mori - &except_vec_vi; +#endif /* CONFIG_MIPS_MT_SMTC */ const int handler_len = &except_vec_vi_end - &except_vec_vi; const int lui_offset = &except_vec_vi_lui - &except_vec_vi; const int ori_offset = &except_vec_vi_ori - &except_vec_vi; @@ -1068,6 +1121,12 @@ static void *set_vi_srs_handler(int n, void *addr, int srs) } memcpy (b, &except_vec_vi, handler_len); +#ifdef CONFIG_MIPS_MT_SMTC + if (n > 7) + printk("Vector index %d exceeds SMTC maximum\n", n); + w = (u32 *)(b + mori_offset); + *w = (*w & 0xffff0000) | (0x100 << n); +#endif /* CONFIG_MIPS_MT_SMTC */ w = (u32 *)(b + lui_offset); *w = (*w & 0xffff0000) | (((u32)handler >> 16) & 0xffff); w = (u32 *)(b + ori_offset); @@ -1090,7 +1149,7 @@ static void *set_vi_srs_handler(int n, void *addr, int srs) return (void *)old_handler; } -void *set_vi_handler (int n, void *addr) +void *set_vi_handler(int n, void *addr) { return set_vi_srs_handler(n, addr, 0); } @@ -1108,8 +1167,29 @@ extern asmlinkage int _restore_fp_context(struct sigcontext *sc); extern asmlinkage int fpu_emulator_save_context(struct sigcontext *sc); extern asmlinkage int fpu_emulator_restore_context(struct sigcontext *sc); +#ifdef CONFIG_SMP +static int smp_save_fp_context(struct sigcontext *sc) +{ + return cpu_has_fpu + ? _save_fp_context(sc) + : fpu_emulator_save_context(sc); +} + +static int smp_restore_fp_context(struct sigcontext *sc) +{ + return cpu_has_fpu + ? _restore_fp_context(sc) + : fpu_emulator_restore_context(sc); +} +#endif + static inline void signal_init(void) { +#ifdef CONFIG_SMP + /* For now just do the cpu_has_fpu check when the functions are invoked */ + save_fp_context = smp_save_fp_context; + restore_fp_context = smp_restore_fp_context; +#else if (cpu_has_fpu) { save_fp_context = _save_fp_context; restore_fp_context = _restore_fp_context; @@ -1117,6 +1197,7 @@ static inline void signal_init(void) save_fp_context = fpu_emulator_save_context; restore_fp_context = fpu_emulator_restore_context; } +#endif } #ifdef CONFIG_MIPS32_COMPAT @@ -1153,6 +1234,20 @@ void __init per_cpu_trap_init(void) { unsigned int cpu = smp_processor_id(); unsigned int status_set = ST0_CU0; +#ifdef CONFIG_MIPS_MT_SMTC + int secondaryTC = 0; + int bootTC = (cpu == 0); + + /* + * Only do per_cpu_trap_init() for first TC of Each VPE. + * Note that this hack assumes that the SMTC init code + * assigns TCs consecutively and in ascending order. + */ + + if (((read_c0_tcbind() & TCBIND_CURTC) != 0) && + ((read_c0_tcbind() & TCBIND_CURVPE) == cpu_data[cpu - 1].vpe_id)) + secondaryTC = 1; +#endif /* CONFIG_MIPS_MT_SMTC */ /* * Disable coprocessors and select 32-bit or 64-bit addressing @@ -1175,6 +1270,10 @@ void __init per_cpu_trap_init(void) write_c0_hwrena (0x0000000f); /* Allow rdhwr to all registers */ #endif +#ifdef CONFIG_MIPS_MT_SMTC + if (!secondaryTC) { +#endif /* CONFIG_MIPS_MT_SMTC */ + /* * Interrupt handling. */ @@ -1191,6 +1290,9 @@ void __init per_cpu_trap_init(void) } else set_c0_cause(CAUSEF_IV); } +#ifdef CONFIG_MIPS_MT_SMTC + } +#endif /* CONFIG_MIPS_MT_SMTC */ cpu_data[cpu].asid_cache = ASID_FIRST_VERSION; TLBMISS_HANDLER_SETUP(); @@ -1200,8 +1302,14 @@ void __init per_cpu_trap_init(void) BUG_ON(current->mm); enter_lazy_tlb(&init_mm, current); - cpu_cache_init(); - tlb_init(); +#ifdef CONFIG_MIPS_MT_SMTC + if (bootTC) { +#endif /* CONFIG_MIPS_MT_SMTC */ + cpu_cache_init(); + tlb_init(); +#ifdef CONFIG_MIPS_MT_SMTC + } +#endif /* CONFIG_MIPS_MT_SMTC */ } /* Install CPU exception handler */ diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S index 2ad0cedf29fe..14fa00e3cdfa 100644 --- a/arch/mips/kernel/vmlinux.lds.S +++ b/arch/mips/kernel/vmlinux.lds.S @@ -2,7 +2,7 @@ #include #include -#undef mips /* CPP really sucks for this job */ +#undef mips #define mips mips OUTPUT_ARCH(mips) ENTRY(kernel_entry) diff --git a/arch/mips/mips-boards/generic/init.c b/arch/mips/mips-boards/generic/init.c index eab5a705e989..17dfe6a8cab9 100644 --- a/arch/mips/mips-boards/generic/init.c +++ b/arch/mips/mips-boards/generic/init.c @@ -220,7 +220,6 @@ void __init kgdb_config (void) generic_putDebugChar (*s++); } - kgdb_enabled = 1; /* Breakpoint is invoked after interrupts are initialised */ } } diff --git a/arch/mips/mips-boards/generic/time.c b/arch/mips/mips-boards/generic/time.c index 93f3bf2c2b22..a9f6124b3a22 100644 --- a/arch/mips/mips-boards/generic/time.c +++ b/arch/mips/mips-boards/generic/time.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -50,16 +51,23 @@ unsigned long cpu_khz; static char display_string[] = " LINUX ON ATLAS "; #endif #if defined(CONFIG_MIPS_MALTA) +#if defined(CONFIG_MIPS_MT_SMTC) +static char display_string[] = " SMTC LINUX ON MALTA "; +#else static char display_string[] = " LINUX ON MALTA "; +#endif /* CONFIG_MIPS_MT_SMTC */ #endif #if defined(CONFIG_MIPS_SEAD) static char display_string[] = " LINUX ON SEAD "; #endif -static unsigned int display_count = 0; +static unsigned int display_count; #define MAX_DISPLAY_COUNT (sizeof(display_string) - 8) -static unsigned int timer_tick_count=0; +#define CPUCTR_IMASKBIT (0x100 << MIPSCPU_INT_CPUCTR) + +static unsigned int timer_tick_count; static int mips_cpu_timer_irq; +extern void smtc_timer_broadcast(int); static inline void scroll_display_message(void) { @@ -75,15 +83,55 @@ static void mips_timer_dispatch (struct pt_regs *regs) do_IRQ (mips_cpu_timer_irq, regs); } +/* + * Redeclare until I get around mopping the timer code insanity on MIPS. + */ extern int null_perf_irq(struct pt_regs *regs); extern int (*perf_irq)(struct pt_regs *regs); irqreturn_t mips_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - int r2 = cpu_has_mips_r2; int cpu = smp_processor_id(); + int r2 = cpu_has_mips_r2; + +#ifdef CONFIG_MIPS_MT_SMTC + /* + * In an SMTC system, one Count/Compare set exists per VPE. + * Which TC within a VPE gets the interrupt is essentially + * random - we only know that it shouldn't be one with + * IXMT set. Whichever TC gets the interrupt needs to + * send special interprocessor interrupts to the other + * TCs to make sure that they schedule, etc. + * + * That code is specific to the SMTC kernel, not to + * the a particular platform, so it's invoked from + * the general MIPS timer_interrupt routine. + */ + + /* + * DVPE is necessary so long as cross-VPE interrupts + * are done via read-modify-write of Cause register. + */ + int vpflags = dvpe(); + write_c0_compare (read_c0_count() - 1); + clear_c0_cause(CPUCTR_IMASKBIT); + evpe(vpflags); + + if (cpu_data[cpu].vpe_id == 0) { + timer_interrupt(irq, dev_id, regs); + scroll_display_message(); + } else + write_c0_compare (read_c0_count() + ( mips_hpt_frequency/HZ)); + smtc_timer_broadcast(cpu_data[cpu].vpe_id); + if (cpu != 0) + /* + * Other CPUs should do profiling and process accounting + */ + local_timer_interrupt(irq, dev_id, regs); + +#else /* CONFIG_MIPS_MT_SMTC */ if (cpu == 0) { /* * CPU 0 handles the global timer interrupt job and process @@ -107,12 +155,14 @@ irqreturn_t mips_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) * More support needs to be added to kernel/time for * counter/timer interrupts on multiple CPU's */ - write_c0_compare (read_c0_count() + (mips_hpt_frequency/HZ)); + write_c0_compare(read_c0_count() + (mips_hpt_frequency/HZ)); + /* - * other CPUs should do profiling and process accounting + * Other CPUs should do profiling and process accounting */ - local_timer_interrupt (irq, dev_id, regs); + local_timer_interrupt(irq, dev_id, regs); } +#endif /* CONFIG_MIPS_MT_SMTC */ out: return IRQ_HANDLED; @@ -126,7 +176,7 @@ static unsigned int __init estimate_cpu_frequency(void) unsigned int prid = read_c0_prid() & 0xffff00; unsigned int count; -#ifdef CONFIG_MIPS_SEAD +#if defined(CONFIG_MIPS_SEAD) || defined(CONFIG_MIPS_SIM) /* * The SEAD board doesn't have a real time clock, so we can't * really calculate the timer frequency @@ -211,7 +261,11 @@ void __init mips_timer_setup(struct irqaction *irq) /* we are using the cpu counter for timer interrupts */ irq->handler = mips_timer_interrupt; /* we use our own handler */ +#ifdef CONFIG_MIPS_MT_SMTC + setup_irq_smtc(mips_cpu_timer_irq, irq, CPUCTR_IMASKBIT); +#else setup_irq(mips_cpu_timer_irq, irq); +#endif /* CONFIG_MIPS_MT_SMTC */ #ifdef CONFIG_SMP /* irq_desc(riptor) is a global resource, when the interrupt overlaps diff --git a/arch/mips/mips-boards/malta/Makefile b/arch/mips/mips-boards/malta/Makefile index fd4c143c0e2f..77ee5c6d33c1 100644 --- a/arch/mips/mips-boards/malta/Makefile +++ b/arch/mips/mips-boards/malta/Makefile @@ -20,3 +20,4 @@ # obj-y := malta_int.o malta_setup.o +obj-$(CONFIG_SMP) += malta_smp.o diff --git a/arch/mips/mips-boards/malta/malta_int.c b/arch/mips/mips-boards/malta/malta_int.c index 1da8c18b9c8e..64db07d4dbe5 100644 --- a/arch/mips/mips-boards/malta/malta_int.c +++ b/arch/mips/mips-boards/malta/malta_int.c @@ -118,8 +118,9 @@ static void malta_hw0_irqdispatch(struct pt_regs *regs) int irq; irq = get_int(); - if (irq < 0) + if (irq < 0) { return; /* interrupt has already been cleared */ + } do_IRQ(MALTA_INT_BASE+irq, regs); } @@ -324,9 +325,15 @@ void __init arch_init_irq(void) else if (cpu_has_vint) { set_vi_handler (MIPSCPU_INT_I8259A, malta_hw0_irqdispatch); set_vi_handler (MIPSCPU_INT_COREHI, corehi_irqdispatch); - +#ifdef CONFIG_MIPS_MT_SMTC + setup_irq_smtc (MIPSCPU_INT_BASE+MIPSCPU_INT_I8259A, &i8259irq, + (0x100 << MIPSCPU_INT_I8259A)); + setup_irq_smtc (MIPSCPU_INT_BASE+MIPSCPU_INT_COREHI, + &corehi_irqaction, (0x100 << MIPSCPU_INT_COREHI)); +#else /* Not SMTC */ setup_irq (MIPSCPU_INT_BASE+MIPSCPU_INT_I8259A, &i8259irq); setup_irq (MIPSCPU_INT_BASE+MIPSCPU_INT_COREHI, &corehi_irqaction); +#endif /* CONFIG_MIPS_MT_SMTC */ } else { setup_irq (MIPSCPU_INT_BASE+MIPSCPU_INT_I8259A, &i8259irq); diff --git a/arch/mips/mips-boards/malta/malta_smp.c b/arch/mips/mips-boards/malta/malta_smp.c new file mode 100644 index 000000000000..6c6c8eeedbce --- /dev/null +++ b/arch/mips/mips-boards/malta/malta_smp.c @@ -0,0 +1,128 @@ +/* + * Malta Platform-specific hooks for SMP operation + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_MIPS_MT_SMTC +#include +#endif /* CONFIG_MIPS_MT_SMTC */ + +/* VPE/SMP Prototype implements platform interfaces directly */ +#if !defined(CONFIG_MIPS_MT_SMP) + +/* + * Cause the specified action to be performed on a targeted "CPU" + */ + +void core_send_ipi(int cpu, unsigned int action) +{ +/* "CPU" may be TC of same VPE, VPE of same CPU, or different CPU */ +#ifdef CONFIG_MIPS_MT_SMTC + smtc_send_ipi(cpu, LINUX_SMP_IPI, action); +#endif /* CONFIG_MIPS_MT_SMTC */ +} + +/* + * Detect available CPUs/VPEs/TCs and populate phys_cpu_present_map + */ + +void __init prom_build_cpu_map(void) +{ + int nextslot; + + /* + * As of November, 2004, MIPSsim only simulates one core + * at a time. However, that core may be a MIPS MT core + * with multiple virtual processors and thread contexts. + */ + + if (read_c0_config3() & (1<<2)) { + nextslot = mipsmt_build_cpu_map(1); + } +} + +/* + * Platform "CPU" startup hook + */ + +void prom_boot_secondary(int cpu, struct task_struct *idle) +{ +#ifdef CONFIG_MIPS_MT_SMTC + smtc_boot_secondary(cpu, idle); +#endif /* CONFIG_MIPS_MT_SMTC */ +} + +/* + * Post-config but pre-boot cleanup entry point + */ + +void prom_init_secondary(void) +{ +#ifdef CONFIG_MIPS_MT_SMTC + void smtc_init_secondary(void); + int myvpe; + + /* Don't enable Malta I/O interrupts (IP2) for secondary VPEs */ + myvpe = read_c0_tcbind() & TCBIND_CURVPE; + if (myvpe != 0) { + /* Ideally, this should be done only once per VPE, but... */ + clear_c0_status(STATUSF_IP2); + set_c0_status(STATUSF_IP0 | STATUSF_IP1 | STATUSF_IP3 + | STATUSF_IP4 | STATUSF_IP5 | STATUSF_IP6 + | STATUSF_IP7); + } + + smtc_init_secondary(); +#endif /* CONFIG_MIPS_MT_SMTC */ +} + +/* + * Platform SMP pre-initialization + * + * As noted above, we can assume a single CPU for now + * but it may be multithreaded. + */ + +void plat_smp_setup(void) +{ + if (read_c0_config3() & (1<<2)) + mipsmt_build_cpu_map(0); +} + +void __init plat_prepare_cpus(unsigned int max_cpus) +{ + if (read_c0_config3() & (1<<2)) + mipsmt_prepare_cpus(); +} + +/* + * SMP initialization finalization entry point + */ + +void prom_smp_finish(void) +{ +#ifdef CONFIG_MIPS_MT_SMTC + smtc_smp_finish(); +#endif /* CONFIG_MIPS_MT_SMTC */ +} + +/* + * Hook for after all CPUs are online + */ + +void prom_cpus_done(void) +{ +} + +#endif /* CONFIG_MIPS32R2_MT_SMP */ diff --git a/arch/mips/mips-boards/sim/cmdline.c b/arch/mips/mips-boards/sim/cmdline.c deleted file mode 100644 index fef9fbd8e710..000000000000 --- a/arch/mips/mips-boards/sim/cmdline.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Carsten Langgaard, carstenl@mips.com - * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * Kernel command line creation using the prom monitor (YAMON) argc/argv. - */ -#include -#include - -#include - -extern int prom_argc; -extern int *_prom_argv; - -/* - * YAMON (32-bit PROM) pass arguments and environment as 32-bit pointer. - * This macro take care of sign extension. - */ -#define prom_argv(index) ((char *)(((int *)(int)_prom_argv)[(index)])) - -char arcs_cmdline[CL_SIZE]; - -char * __init prom_getcmdline(void) -{ - return &(arcs_cmdline[0]); -} - - -void __init prom_init_cmdline(void) -{ - char *cp; - int actr; - - actr = 1; /* Always ignore argv[0] */ - - cp = &(arcs_cmdline[0]); - while(actr < prom_argc) { - strcpy(cp, prom_argv(actr)); - cp += strlen(prom_argv(actr)); - *cp++ = ' '; - actr++; - } - if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */ - --cp; - *cp = '\0'; -} diff --git a/arch/mips/mips-boards/sim/sim_cmdline.c b/arch/mips/mips-boards/sim/sim_cmdline.c index 9df37c6fca36..c63021a5dc6c 100644 --- a/arch/mips/mips-boards/sim/sim_cmdline.c +++ b/arch/mips/mips-boards/sim/sim_cmdline.c @@ -26,8 +26,10 @@ char * __init prom_getcmdline(void) return arcs_cmdline; } - void __init prom_init_cmdline(void) { - /* nothing to do */ + char *cp; + cp = arcs_cmdline; + /* Get boot line from environment? */ + *cp = '\0'; } diff --git a/arch/mips/mips-boards/sim/sim_smp.c b/arch/mips/mips-boards/sim/sim_smp.c index a9f0c2bfe4ad..b7084e7c4bf9 100644 --- a/arch/mips/mips-boards/sim/sim_smp.c +++ b/arch/mips/mips-boards/sim/sim_smp.c @@ -44,8 +44,6 @@ void core_send_ipi(int cpu, unsigned int action) { #ifdef CONFIG_MIPS_MT_SMTC - void smtc_send_ipi(int, int, unsigned int); - smtc_send_ipi(cpu, LINUX_SMP_IPI, action); #endif /* CONFIG_MIPS_MT_SMTC */ /* "CPU" may be TC of same VPE, VPE of same CPU, or different CPU */ @@ -59,15 +57,8 @@ void core_send_ipi(int cpu, unsigned int action) void __init prom_build_cpu_map(void) { #ifdef CONFIG_MIPS_MT_SMTC - extern int mipsmt_build_cpu_map(int startslot); int nextslot; - cpus_clear(phys_cpu_present_map); - - /* Register the boot CPU */ - - smp_prepare_boot_cpu(); - /* * As of November, 2004, MIPSsim only simulates one core * at a time. However, that core may be a MIPS MT core @@ -87,8 +78,6 @@ void __init prom_build_cpu_map(void) void prom_boot_secondary(int cpu, struct task_struct *idle) { #ifdef CONFIG_MIPS_MT_SMTC - extern void smtc_boot_secondary(int cpu, struct task_struct *t); - smtc_boot_secondary(cpu, idle); #endif /* CONFIG_MIPS_MT_SMTC */ } @@ -113,7 +102,6 @@ void prom_init_secondary(void) void prom_prepare_cpus(unsigned int max_cpus) { #ifdef CONFIG_MIPS_MT_SMTC - void mipsmt_prepare_cpus(int c); /* * As noted above, we can assume a single CPU for now * but it may be multithreaded. @@ -132,8 +120,6 @@ void prom_prepare_cpus(unsigned int max_cpus) void prom_smp_finish(void) { #ifdef CONFIG_MIPS_MT_SMTC - void smtc_smp_finish(void); - smtc_smp_finish(); #endif /* CONFIG_MIPS_MT_SMTC */ } diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c index 2d9624fd10ec..e3a617224868 100644 --- a/arch/mips/mm/fault.c +++ b/arch/mips/mm/fault.c @@ -157,7 +157,6 @@ no_context: * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ - bust_spinlocks(1); printk(KERN_ALERT "CPU %d Unable to handle kernel paging request at " @@ -188,11 +187,20 @@ do_sigbus: /* Kernel mode? Handle exceptions or die */ if (!user_mode(regs)) goto no_context; - + else /* * Send a sigbus, regardless of whether we were in kernel * or user mode. */ +#if 0 + printk("do_page_fault() #3: sending SIGBUS to %s for " + "invalid %s\n%0*lx (epc == %0*lx, ra == %0*lx)\n", + tsk->comm, + write ? "write access to" : "read access from", + field, address, + field, (unsigned long) regs->cp0_epc, + field, (unsigned long) regs->regs[31]); +#endif tsk->thread.cp0_badvaddr = address; info.si_signo = SIGBUS; info.si_errno = 0; @@ -201,7 +209,6 @@ do_sigbus: force_sig_info(SIGBUS, &info, tsk); return; - vmalloc_fault: { /* diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c index a865f2394cb0..9dca099ba16b 100644 --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c @@ -32,13 +32,35 @@ extern void build_tlb_refill_handler(void); "nop; nop; nop; nop; nop; nop;\n\t" \ ".set reorder\n\t") +/* Atomicity and interruptability */ +#ifdef CONFIG_MIPS_MT_SMTC + +#include +#include + +#define ENTER_CRITICAL(flags) \ + { \ + unsigned int mvpflags; \ + local_irq_save(flags);\ + mvpflags = dvpe() +#define EXIT_CRITICAL(flags) \ + evpe(mvpflags); \ + local_irq_restore(flags); \ + } +#else + +#define ENTER_CRITICAL(flags) local_irq_save(flags) +#define EXIT_CRITICAL(flags) local_irq_restore(flags) + +#endif /* CONFIG_MIPS_MT_SMTC */ + void local_flush_tlb_all(void) { unsigned long flags; unsigned long old_ctx; int entry; - local_irq_save(flags); + ENTER_CRITICAL(flags); /* Save old context and create impossible VPN2 value */ old_ctx = read_c0_entryhi(); write_c0_entrylo0(0); @@ -57,7 +79,7 @@ void local_flush_tlb_all(void) } tlbw_use_hazard(); write_c0_entryhi(old_ctx); - local_irq_restore(flags); + EXIT_CRITICAL(flags); } /* All entries common to a mm share an asid. To effectively flush @@ -87,6 +109,7 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long flags; int size; + ENTER_CRITICAL(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; size = (size + 1) >> 1; local_irq_save(flags); @@ -120,7 +143,7 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, } else { drop_mmu_context(mm, cpu); } - local_irq_restore(flags); + EXIT_CRITICAL(flags); } } @@ -129,9 +152,9 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) unsigned long flags; int size; + ENTER_CRITICAL(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; size = (size + 1) >> 1; - local_irq_save(flags); if (size <= current_cpu_data.tlbsize / 2) { int pid = read_c0_entryhi(); @@ -162,7 +185,7 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) } else { local_flush_tlb_all(); } - local_irq_restore(flags); + EXIT_CRITICAL(flags); } void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) @@ -175,7 +198,7 @@ void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) newpid = cpu_asid(cpu, vma->vm_mm); page &= (PAGE_MASK << 1); - local_irq_save(flags); + ENTER_CRITICAL(flags); oldpid = read_c0_entryhi(); write_c0_entryhi(page | newpid); mtc0_tlbw_hazard(); @@ -194,7 +217,7 @@ void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) finish: write_c0_entryhi(oldpid); - local_irq_restore(flags); + EXIT_CRITICAL(flags); } } @@ -207,7 +230,7 @@ void local_flush_tlb_one(unsigned long page) unsigned long flags; int oldpid, idx; - local_irq_save(flags); + ENTER_CRITICAL(flags); oldpid = read_c0_entryhi(); page &= (PAGE_MASK << 1); write_c0_entryhi(page); @@ -226,7 +249,7 @@ void local_flush_tlb_one(unsigned long page) } write_c0_entryhi(oldpid); - local_irq_restore(flags); + EXIT_CRITICAL(flags); } /* @@ -249,7 +272,7 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte) if (current->active_mm != vma->vm_mm) return; - local_irq_save(flags); + ENTER_CRITICAL(flags); pid = read_c0_entryhi() & ASID_MASK; address &= (PAGE_MASK << 1); @@ -277,7 +300,7 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte) else tlb_write_indexed(); tlbw_use_hazard(); - local_irq_restore(flags); + EXIT_CRITICAL(flags); } #if 0 @@ -291,7 +314,7 @@ static void r4k_update_mmu_cache_hwbug(struct vm_area_struct * vma, pte_t *ptep; int idx; - local_irq_save(flags); + ENTER_CRITICAL(flags); address &= (PAGE_MASK << 1); asid = read_c0_entryhi() & ASID_MASK; write_c0_entryhi(address | asid); @@ -310,7 +333,7 @@ static void r4k_update_mmu_cache_hwbug(struct vm_area_struct * vma, else tlb_write_indexed(); tlbw_use_hazard(); - local_irq_restore(flags); + EXIT_CRITICAL(flags); } #endif @@ -322,7 +345,7 @@ void __init add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, unsigned long old_pagemask; unsigned long old_ctx; - local_irq_save(flags); + ENTER_CRITICAL(flags); /* Save old context and create impossible VPN2 value */ old_ctx = read_c0_entryhi(); old_pagemask = read_c0_pagemask(); @@ -342,7 +365,7 @@ void __init add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, BARRIER; write_c0_pagemask(old_pagemask); local_flush_tlb_all(); - local_irq_restore(flags); + EXIT_CRITICAL(flags); } /* @@ -362,7 +385,7 @@ __init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, unsigned long old_pagemask; unsigned long old_ctx; - local_irq_save(flags); + ENTER_CRITICAL(flags); /* Save old context and create impossible VPN2 value */ old_ctx = read_c0_entryhi(); old_pagemask = read_c0_pagemask(); @@ -386,10 +409,11 @@ __init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, write_c0_entryhi(old_ctx); write_c0_pagemask(old_pagemask); out: - local_irq_restore(flags); + EXIT_CRITICAL(flags); return ret; } +extern void __init sanitize_tlb_entries(void); static void __init probe_tlb(unsigned long config) { struct cpuinfo_mips *c = ¤t_cpu_data; @@ -402,6 +426,14 @@ static void __init probe_tlb(unsigned long config) */ if ((c->processor_id & 0xff0000) == PRID_COMP_LEGACY) return; +#ifdef CONFIG_MIPS_MT_SMTC + /* + * If TLB is shared in SMTC system, total size already + * has been calculated and written into cpu_data tlbsize + */ + if((smtc_status & SMTC_TLB_SHARED) == SMTC_TLB_SHARED) + return; +#endif /* CONFIG_MIPS_MT_SMTC */ reg = read_c0_config1(); if (!((config >> 7) & 3)) @@ -410,6 +442,15 @@ static void __init probe_tlb(unsigned long config) c->tlbsize = ((reg >> 25) & 0x3f) + 1; } +static int __initdata ntlb = 0; +static int __init set_ntlb(char *str) +{ + get_option(&str, &ntlb); + return 1; +} + +__setup("ntlb=", set_ntlb); + void __init tlb_init(void) { unsigned int config = read_c0_config(); @@ -432,5 +473,15 @@ void __init tlb_init(void) /* Did I tell you that ARC SUCKS? */ + if (ntlb) { + if (ntlb > 1 && ntlb <= current_cpu_data.tlbsize) { + int wired = current_cpu_data.tlbsize - ntlb; + write_c0_wired(wired); + write_c0_index(wired-1); + printk ("Restricting TLB to %d entries\n", ntlb); + } else + printk("Ignoring invalid argument ntlb=%d\n", ntlb); + } + build_tlb_refill_handler(); } diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index c5eea6ae12ca..053dbacac56b 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -7,6 +7,16 @@ * * Copyright (C) 2004,2005 by Thiemo Seufer * Copyright (C) 2005 Maciej W. Rozycki + * Copyright (C) 2006 Ralf Baechle (ralf@linux-mips.org) + * + * ... and the days got worse and worse and now you see + * I've gone completly out of my mind. + * + * They're coming to take me a away haha + * they're coming to take me a away hoho hihi haha + * to the funny farm where code is beautiful all the time ... + * + * (Condolences to Napoleon XIV) */ #include @@ -68,6 +78,7 @@ enum fields BIMM = 0x040, JIMM = 0x080, FUNC = 0x100, + SET = 0x200 }; #define OP_MASK 0x2f @@ -86,6 +97,8 @@ enum fields #define JIMM_SH 0 #define FUNC_MASK 0x2f #define FUNC_SH 0 +#define SET_MASK 0x7 +#define SET_SH 0 enum opcode { insn_invalid, @@ -129,8 +142,8 @@ static __initdata struct insn insn_table[] = { { insn_bne, M(bne_op,0,0,0,0,0), RS | RT | BIMM }, { insn_daddiu, M(daddiu_op,0,0,0,0,0), RS | RT | SIMM }, { insn_daddu, M(spec_op,0,0,0,0,daddu_op), RS | RT | RD }, - { insn_dmfc0, M(cop0_op,dmfc_op,0,0,0,0), RT | RD }, - { insn_dmtc0, M(cop0_op,dmtc_op,0,0,0,0), RT | RD }, + { insn_dmfc0, M(cop0_op,dmfc_op,0,0,0,0), RT | RD | SET}, + { insn_dmtc0, M(cop0_op,dmtc_op,0,0,0,0), RT | RD | SET}, { insn_dsll, M(spec_op,0,0,0,0,dsll_op), RT | RD | RE }, { insn_dsll32, M(spec_op,0,0,0,0,dsll32_op), RT | RD | RE }, { insn_dsra, M(spec_op,0,0,0,0,dsra_op), RT | RD | RE }, @@ -145,8 +158,8 @@ static __initdata struct insn insn_table[] = { { insn_lld, M(lld_op,0,0,0,0,0), RS | RT | SIMM }, { insn_lui, M(lui_op,0,0,0,0,0), RT | SIMM }, { insn_lw, M(lw_op,0,0,0,0,0), RS | RT | SIMM }, - { insn_mfc0, M(cop0_op,mfc_op,0,0,0,0), RT | RD }, - { insn_mtc0, M(cop0_op,mtc_op,0,0,0,0), RT | RD }, + { insn_mfc0, M(cop0_op,mfc_op,0,0,0,0), RT | RD | SET}, + { insn_mtc0, M(cop0_op,mtc_op,0,0,0,0), RT | RD | SET}, { insn_ori, M(ori_op,0,0,0,0,0), RS | RT | UIMM }, { insn_rfe, M(cop0_op,cop_op,0,0,0,rfe_op), 0 }, { insn_sc, M(sc_op,0,0,0,0,0), RS | RT | SIMM }, @@ -242,6 +255,14 @@ static __init u32 build_func(u32 arg) return arg & FUNC_MASK; } +static __init u32 build_set(u32 arg) +{ + if (arg & ~SET_MASK) + printk(KERN_WARNING "TLB synthesizer field overflow\n"); + + return arg & SET_MASK; +} + /* * The order of opcode arguments is implicitly left to right, * starting with RS and ending with FUNC or IMM. @@ -273,6 +294,7 @@ static void __init build_insn(u32 **buf, enum opcode opc, ...) if (ip->fields & BIMM) op |= build_bimm(va_arg(ap, s32)); if (ip->fields & JIMM) op |= build_jimm(va_arg(ap, u32)); if (ip->fields & FUNC) op |= build_func(va_arg(ap, u32)); + if (ip->fields & SET) op |= build_set(va_arg(ap, u32)); va_end(ap); **buf = op; @@ -358,8 +380,8 @@ I_u1s2(_bgezl); I_u1s2(_bltz); I_u1s2(_bltzl); I_u1u2s3(_bne); -I_u1u2(_dmfc0); -I_u1u2(_dmtc0); +I_u1u2u3(_dmfc0); +I_u1u2u3(_dmtc0); I_u2u1s3(_daddiu); I_u3u1u2(_daddu); I_u2u1u3(_dsll); @@ -376,8 +398,8 @@ I_u2s3u1(_ll); I_u2s3u1(_lld); I_u1s2(_lui); I_u2s3u1(_lw); -I_u1u2(_mfc0); -I_u1u2(_mtc0); +I_u1u2u3(_mfc0); +I_u1u2u3(_mtc0); I_u2u1u3(_ori); I_0(_rfe); I_u2s3u1(_sc); @@ -451,8 +473,8 @@ L_LA(_r3000_write_probe_fail) # define i_SLL(buf, rs, rt, sh) i_dsll(buf, rs, rt, sh) # define i_SRA(buf, rs, rt, sh) i_dsra(buf, rs, rt, sh) # define i_SRL(buf, rs, rt, sh) i_dsrl(buf, rs, rt, sh) -# define i_MFC0(buf, rt, rd) i_dmfc0(buf, rt, rd) -# define i_MTC0(buf, rt, rd) i_dmtc0(buf, rt, rd) +# define i_MFC0(buf, rt, rd...) i_dmfc0(buf, rt, rd) +# define i_MTC0(buf, rt, rd...) i_dmtc0(buf, rt, rd) # define i_ADDIU(buf, rs, rt, val) i_daddiu(buf, rs, rt, val) # define i_ADDU(buf, rs, rt, rd) i_daddu(buf, rs, rt, rd) # define i_SUBU(buf, rs, rt, rd) i_dsubu(buf, rs, rt, rd) @@ -464,8 +486,8 @@ L_LA(_r3000_write_probe_fail) # define i_SLL(buf, rs, rt, sh) i_sll(buf, rs, rt, sh) # define i_SRA(buf, rs, rt, sh) i_sra(buf, rs, rt, sh) # define i_SRL(buf, rs, rt, sh) i_srl(buf, rs, rt, sh) -# define i_MFC0(buf, rt, rd) i_mfc0(buf, rt, rd) -# define i_MTC0(buf, rt, rd) i_mtc0(buf, rt, rd) +# define i_MFC0(buf, rt, rd...) i_mfc0(buf, rt, rd) +# define i_MTC0(buf, rt, rd...) i_mtc0(buf, rt, rd) # define i_ADDIU(buf, rs, rt, val) i_addiu(buf, rs, rt, val) # define i_ADDU(buf, rs, rt, rd) i_addu(buf, rs, rt, rd) # define i_SUBU(buf, rs, rt, rd) i_subu(buf, rs, rt, rd) @@ -670,14 +692,15 @@ static void __init il_bgezl(u32 **p, struct reloc **r, unsigned int reg, #define K1 27 /* Some CP0 registers */ -#define C0_INDEX 0 -#define C0_ENTRYLO0 2 -#define C0_ENTRYLO1 3 -#define C0_CONTEXT 4 -#define C0_BADVADDR 8 -#define C0_ENTRYHI 10 -#define C0_EPC 14 -#define C0_XCONTEXT 20 +#define C0_INDEX 0, 0 +#define C0_ENTRYLO0 2, 0 +#define C0_TCBIND 2, 2 +#define C0_ENTRYLO1 3, 0 +#define C0_CONTEXT 4, 0 +#define C0_BADVADDR 8, 0 +#define C0_ENTRYHI 10, 0 +#define C0_EPC 14, 0 +#define C0_XCONTEXT 20, 0 #ifdef CONFIG_64BIT # define GET_CONTEXT(buf, reg) i_MFC0(buf, reg, C0_XCONTEXT) @@ -951,12 +974,20 @@ build_get_pmde64(u32 **p, struct label **l, struct reloc **r, /* No i_nop needed here, since the next insn doesn't touch TMP. */ #ifdef CONFIG_SMP +# ifdef CONFIG_MIPS_MT_SMTC + /* + * SMTC uses TCBind value as "CPU" index + */ + i_mfc0(p, ptr, C0_TCBIND); + i_dsrl(p, ptr, ptr, 19); +# else /* * 64 bit SMP running in XKPHYS has smp_processor_id() << 3 * stored in CONTEXT. */ i_dmfc0(p, ptr, C0_CONTEXT); i_dsrl(p, ptr, ptr, 23); +#endif i_LA_mostly(p, tmp, pgdc); i_daddu(p, ptr, ptr, tmp); i_dmfc0(p, tmp, C0_BADVADDR); @@ -1014,9 +1045,21 @@ build_get_pgde32(u32 **p, unsigned int tmp, unsigned int ptr) /* 32 bit SMP has smp_processor_id() stored in CONTEXT. */ #ifdef CONFIG_SMP +#ifdef CONFIG_MIPS_MT_SMTC + /* + * SMTC uses TCBind value as "CPU" index + */ + i_mfc0(p, ptr, C0_TCBIND); + i_LA_mostly(p, tmp, pgdc); + i_srl(p, ptr, ptr, 19); +#else + /* + * smp_processor_id() << 3 is stored in CONTEXT. + */ i_mfc0(p, ptr, C0_CONTEXT); i_LA_mostly(p, tmp, pgdc); i_srl(p, ptr, ptr, 23); +#endif i_addu(p, ptr, tmp, ptr); #else i_LA_mostly(p, ptr, pgdc); diff --git a/include/asm-mips/asmmacro.h b/include/asm-mips/asmmacro.h index 30b18ea6cb11..f54aa147ec19 100644 --- a/include/asm-mips/asmmacro.h +++ b/include/asm-mips/asmmacro.h @@ -17,7 +17,26 @@ #ifdef CONFIG_64BIT #include #endif +#ifdef CONFIG_MIPS_MT_SMTC +#include +#endif +#ifdef CONFIG_MIPS_MT_SMTC + .macro local_irq_enable reg=t0 + mfc0 \reg, CP0_TCSTATUS + ori \reg, \reg, TCSTATUS_IXMT + xori \reg, \reg, TCSTATUS_IXMT + mtc0 \reg, CP0_TCSTATUS + ehb + .endm + + .macro local_irq_disable reg=t0 + mfc0 \reg, CP0_TCSTATUS + ori \reg, \reg, TCSTATUS_IXMT + mtc0 \reg, CP0_TCSTATUS + ehb + .endm +#else .macro local_irq_enable reg=t0 mfc0 \reg, CP0_STATUS ori \reg, \reg, 1 @@ -32,6 +51,7 @@ mtc0 \reg, CP0_STATUS irq_disable_hazard .endm +#endif /* CONFIG_MIPS_MT_SMTC */ #ifdef CONFIG_CPU_SB1 .macro fpu_enable_hazard @@ -48,4 +68,31 @@ .endm #endif +/* + * Temporary until all gas have MT ASE support + */ + .macro DMT reg=0 + .word (0x41600bc1 | (\reg << 16)) + .endm + + .macro EMT reg=0 + .word (0x41600be1 | (\reg << 16)) + .endm + + .macro DVPE reg=0 + .word (0x41600001 | (\reg << 16)) + .endm + + .macro EVPE reg=0 + .word (0x41600021 | (\reg << 16)) + .endm + + .macro MFTR rt=0, rd=0, u=0, sel=0 + .word (0x41000000 | (\rt << 16) | (\rd << 11) | (\u << 5) | (\sel)) + .endm + + .macro MTTR rt=0, rd=0, u=0, sel=0 + .word (0x41800000 | (\rt << 16) | (\rd << 11) | (\u << 5) | (\sel)) + .endm + #endif /* _ASM_ASMMACRO_H */ diff --git a/include/asm-mips/cpu-info.h b/include/asm-mips/cpu-info.h index 140be1c67da7..6572ac703662 100644 --- a/include/asm-mips/cpu-info.h +++ b/include/asm-mips/cpu-info.h @@ -73,6 +73,16 @@ struct cpuinfo_mips { struct cache_desc dcache; /* Primary D or combined I/D cache */ struct cache_desc scache; /* Secondary cache */ struct cache_desc tcache; /* Tertiary/split secondary cache */ +#if defined(CONFIG_MIPS_MT_SMTC) + /* + * In the MIPS MT "SMTC" model, each TC is considered + * to be a "CPU" for the purposes of scheduling, but + * exception resources, ASID spaces, etc, are common + * to all TCs within the same VPE. + */ + int vpe_id; /* Virtual Processor number */ + int tc_id; /* Thread Context number */ +#endif /* CONFIG_MIPS_MT */ void *data; /* Additional data */ } __attribute__((aligned(SMP_CACHE_BYTES))); diff --git a/include/asm-mips/hazards.h b/include/asm-mips/hazards.h index feb29a793888..dadc05188db7 100644 --- a/include/asm-mips/hazards.h +++ b/include/asm-mips/hazards.h @@ -284,6 +284,8 @@ do { \ #define instruction_hazard() do { } while (0) #endif +extern void mips_ihb(void); + #endif /* __ASSEMBLY__ */ #endif /* _ASM_HAZARDS_H */ diff --git a/include/asm-mips/interrupt.h b/include/asm-mips/interrupt.h index 774348734fa0..4bb9c06f4410 100644 --- a/include/asm-mips/interrupt.h +++ b/include/asm-mips/interrupt.h @@ -19,7 +19,12 @@ __asm__ ( " .set push \n" " .set reorder \n" " .set noat \n" -#ifdef CONFIG_CPU_MIPSR2 +#ifdef CONFIG_MIPS_MT_SMTC + " mfc0 $1, $2, 1 # SMTC - clear TCStatus.IXMT \n" + " ori $1, 0x400 \n" + " xori $1, 0x400 \n" + " mtc0 $1, $2, 1 \n" +#elif defined(CONFIG_CPU_MIPSR2) " ei \n" #else " mfc0 $1,$12 \n" @@ -62,7 +67,12 @@ __asm__ ( " .macro local_irq_disable\n" " .set push \n" " .set noat \n" -#ifdef CONFIG_CPU_MIPSR2 +#ifdef CONFIG_MIPS_MT_SMTC + " mfc0 $1, $2, 1 \n" + " ori $1, 0x400 \n" + " .set noreorder \n" + " mtc0 $1, $2, 1 \n" +#elif defined(CONFIG_CPU_MIPSR2) " di \n" #else " mfc0 $1,$12 \n" @@ -88,7 +98,11 @@ __asm__ ( " .macro local_save_flags flags \n" " .set push \n" " .set reorder \n" +#ifdef CONFIG_MIPS_MT_SMTC + " mfc0 \\flags, $2, 1 \n" +#else " mfc0 \\flags, $12 \n" +#endif " .set pop \n" " .endm \n"); @@ -102,7 +116,13 @@ __asm__ ( " .set push \n" " .set reorder \n" " .set noat \n" -#ifdef CONFIG_CPU_MIPSR2 +#ifdef CONFIG_MIPS_MT_SMTC + " mfc0 \\result, $2, 1 \n" + " ori $1, \\result, 0x400 \n" + " .set noreorder \n" + " mtc0 $1, $2, 1 \n" + " andi \\result, \\result, 0x400 \n" +#elif defined(CONFIG_CPU_MIPSR2) " di \\result \n" " andi \\result, 1 \n" #else @@ -128,7 +148,14 @@ __asm__ ( " .set push \n" " .set noreorder \n" " .set noat \n" -#if defined(CONFIG_CPU_MIPSR2) && defined(CONFIG_IRQ_CPU) +#ifdef CONFIG_MIPS_MT_SMTC + "mfc0 $1, $2, 1 \n" + "andi \\flags, 0x400 \n" + "ori $1, 0x400 \n" + "xori $1, 0x400 \n" + "or \\flags, $1 \n" + "mtc0 \\flags, $2, 1 \n" +#elif defined(CONFIG_CPU_MIPSR2) && defined(CONFIG_IRQ_CPU) /* * Slow, but doesn't suffer from a relativly unlikely race * condition we're having since days 1. @@ -167,11 +194,29 @@ do { \ : "memory"); \ } while(0) -#define irqs_disabled() \ -({ \ - unsigned long flags; \ - local_save_flags(flags); \ - !(flags & 1); \ -}) +static inline int irqs_disabled(void) +{ +#ifdef CONFIG_MIPS_MT_SMTC + /* + * SMTC model uses TCStatus.IXMT to disable interrupts for a thread/CPU + */ + unsigned long __result; + + __asm__ __volatile__( + " .set noreorder \n" + " mfc0 %0, $2, 1 \n" + " andi %0, 0x400 \n" + " slt %0, $0, %0 \n" + " .set reorder \n" + : "=r" (__result)); + + return __result; +#else + unsigned long flags; + local_save_flags(flags); + + return !(flags & 1); +#endif +} #endif /* _ASM_INTERRUPT_H */ diff --git a/include/asm-mips/irq.h b/include/asm-mips/irq.h index d7aecca3b95f..dde677f02bc0 100644 --- a/include/asm-mips/irq.h +++ b/include/asm-mips/irq.h @@ -11,6 +11,9 @@ #include #include + +#include + #include #ifdef CONFIG_I8259 @@ -26,6 +29,23 @@ struct pt_regs; extern asmlinkage unsigned int do_IRQ(unsigned int irq, struct pt_regs *regs); +#ifdef CONFIG_MIPS_MT_SMTC +/* + * Clear interrupt mask handling "backstop" if irq_hwmask + * entry so indicates. This implies that the ack() or end() + * functions will take over re-enabling the low-level mask. + * Otherwise it will be done on return from exception. + */ +#define __DO_IRQ_SMTC_HOOK() \ +do { \ + if (irq_hwmask[irq] & 0x0000ff00) \ + write_c0_tccontext(read_c0_tccontext() & \ + ~(irq_hwmask[irq] & 0x0000ff00)); \ +} while (0) +#else +#define __DO_IRQ_SMTC_HOOK() do { } while (0) +#endif + #ifdef CONFIG_PREEMPT /* @@ -39,6 +59,7 @@ extern asmlinkage unsigned int do_IRQ(unsigned int irq, struct pt_regs *regs); #define do_IRQ(irq, regs) \ do { \ irq_enter(); \ + __DO_IRQ_SMTC_HOOK(); \ __do_IRQ((irq), (regs)); \ irq_exit(); \ } while (0) @@ -48,4 +69,12 @@ do { \ extern void arch_init_irq(void); extern void spurious_interrupt(struct pt_regs *regs); +#ifdef CONFIG_MIPS_MT_SMTC +struct irqaction; + +extern unsigned long irq_hwmask[]; +extern int setup_irq_smtc(unsigned int irq, struct irqaction * new, + unsigned long hwmask); +#endif /* CONFIG_MIPS_MT_SMTC */ + #endif /* _ASM_IRQ_H */ diff --git a/include/asm-mips/mips_mt.h b/include/asm-mips/mips_mt.h new file mode 100644 index 000000000000..c31a312b9783 --- /dev/null +++ b/include/asm-mips/mips_mt.h @@ -0,0 +1,15 @@ +/* + * Definitions and decalrations for MIPS MT support + * that are common between SMTC, VSMP, and/or AP/SP + * kernel models. + */ +#ifndef __ASM_MIPS_MT_H +#define __ASM_MIPS_MT_H + +extern cpumask_t mt_fpu_cpumask; +extern unsigned long mt_fpemul_threshold; + +extern void mips_mt_regdump(unsigned long previous_mvpcontrol_value); +extern void mips_mt_set_cpuoptions(void); + +#endif /* __ASM_MIPS_MT_H */ diff --git a/include/asm-mips/mipsmtregs.h b/include/asm-mips/mipsmtregs.h index a5ac1a62f4f4..f637ce70758f 100644 --- a/include/asm-mips/mipsmtregs.h +++ b/include/asm-mips/mipsmtregs.h @@ -165,7 +165,7 @@ #ifndef __ASSEMBLY__ -extern void mips_mt_regdump(void); +extern void mips_mt_regdump(unsigned long previous_mvpcontrol_value); static inline unsigned int dvpe(void) { @@ -282,8 +282,11 @@ static inline void ehb(void) \ __asm__ __volatile__( \ " .set push \n" \ + " .set noat \n" \ " .set mips32r2 \n" \ - " mftgpr %0," #rt " \n" \ + " # mftgpr $1," #rt " \n" \ + " .word 0x41000820 | (" #rt " << 16) \n" \ + " move %0, $1 \n" \ " .set pop \n" \ : "=r" (__res)); \ \ @@ -295,9 +298,7 @@ static inline void ehb(void) unsigned long __res; \ \ __asm__ __volatile__( \ - ".set noat\n\t" \ - "mftr\t%0, " #rt ", " #u ", " #sel "\n\t" \ - ".set at\n\t" \ + " mftr %0, " #rt ", " #u ", " #sel " \n" \ : "=r" (__res)); \ \ __res; \ diff --git a/include/asm-mips/mipsregs.h b/include/asm-mips/mipsregs.h index e85a42e2ea0c..a2ef579f6b1a 100644 --- a/include/asm-mips/mipsregs.h +++ b/include/asm-mips/mipsregs.h @@ -861,7 +861,19 @@ do { \ #define write_c0_compare3(val) __write_32bit_c0_register($11, 7, val) #define read_c0_status() __read_32bit_c0_register($12, 0) +#ifdef CONFIG_MIPS_MT_SMTC +#define write_c0_status(val) \ +do { \ + __write_32bit_c0_register($12, 0, val); \ + __ehb(); \ +} while (0) +#else +/* + * Legacy non-SMTC code, which may be hazardous + * but which might not support EHB + */ #define write_c0_status(val) __write_32bit_c0_register($12, 0, val) +#endif /* CONFIG_MIPS_MT_SMTC */ #define read_c0_cause() __read_32bit_c0_register($13, 0) #define write_c0_cause(val) __write_32bit_c0_register($13, 0, val) @@ -1004,6 +1016,9 @@ do { \ #define read_c0_taglo() __read_32bit_c0_register($28, 0) #define write_c0_taglo(val) __write_32bit_c0_register($28, 0, val) +#define read_c0_dtaglo() __read_32bit_c0_register($28, 2) +#define write_c0_dtaglo(val) __write_32bit_c0_register($28, 2, val) + #define read_c0_taghi() __read_32bit_c0_register($29, 0) #define write_c0_taghi(val) __write_32bit_c0_register($29, 0, val) @@ -1357,6 +1372,11 @@ static inline void tlb_write_random(void) /* * Manipulate bits in a c0 register. */ +#ifndef CONFIG_MIPS_MT_SMTC +/* + * SMTC Linux requires shutting-down microthread scheduling + * during CP0 register read-modify-write sequences. + */ #define __BUILD_SET_C0(name) \ static inline unsigned int \ set_c0_##name(unsigned int set) \ @@ -1395,6 +1415,119 @@ change_c0_##name(unsigned int change, unsigned int new) \ return res; \ } +#else /* SMTC versions that manage MT scheduling */ + +#include + +/* + * This is a duplicate of dmt() in mipsmtregs.h to avoid problems with + * header file recursion. + */ +static inline unsigned int __dmt(void) +{ + int res; + + __asm__ __volatile__( + " .set push \n" + " .set mips32r2 \n" + " .set noat \n" + " .word 0x41610BC1 # dmt $1 \n" + " ehb \n" + " move %0, $1 \n" + " .set pop \n" + : "=r" (res)); + + instruction_hazard(); + + return res; +} + +#define __VPECONTROL_TE_SHIFT 15 +#define __VPECONTROL_TE (1UL << __VPECONTROL_TE_SHIFT) + +#define __EMT_ENABLE __VPECONTROL_TE + +static inline void __emt(unsigned int previous) +{ + if ((previous & __EMT_ENABLE)) + __asm__ __volatile__( + " .set noreorder \n" + " .set mips32r2 \n" + " .word 0x41600be1 # emt \n" + " ehb \n" + " .set mips0 \n" + " .set reorder \n"); +} + +static inline void __ehb(void) +{ + __asm__ __volatile__( + " ehb \n"); +} + +/* + * Note that local_irq_save/restore affect TC-specific IXMT state, + * not Status.IE as in non-SMTC kernel. + */ + +#define __BUILD_SET_C0(name) \ +static inline unsigned int \ +set_c0_##name(unsigned int set) \ +{ \ + unsigned int res; \ + unsigned int omt; \ + unsigned int flags; \ + \ + local_irq_save(flags); \ + omt = __dmt(); \ + res = read_c0_##name(); \ + res |= set; \ + write_c0_##name(res); \ + __emt(omt); \ + local_irq_restore(flags); \ + \ + return res; \ +} \ + \ +static inline unsigned int \ +clear_c0_##name(unsigned int clear) \ +{ \ + unsigned int res; \ + unsigned int omt; \ + unsigned int flags; \ + \ + local_irq_save(flags); \ + omt = __dmt(); \ + res = read_c0_##name(); \ + res &= ~clear; \ + write_c0_##name(res); \ + __emt(omt); \ + local_irq_restore(flags); \ + \ + return res; \ +} \ + \ +static inline unsigned int \ +change_c0_##name(unsigned int change, unsigned int new) \ +{ \ + unsigned int res; \ + unsigned int omt; \ + unsigned int flags; \ + \ + local_irq_save(flags); \ + \ + omt = __dmt(); \ + res = read_c0_##name(); \ + res &= ~change; \ + res |= (new & change); \ + write_c0_##name(res); \ + __emt(omt); \ + local_irq_restore(flags); \ + \ + return res; \ +} +#endif + __BUILD_SET_C0(status) __BUILD_SET_C0(cause) __BUILD_SET_C0(config) diff --git a/include/asm-mips/mmu_context.h b/include/asm-mips/mmu_context.h index 61cf22588137..6e09f4c87211 100644 --- a/include/asm-mips/mmu_context.h +++ b/include/asm-mips/mmu_context.h @@ -17,6 +17,10 @@ #include #include #include +#ifdef CONFIG_MIPS_MT_SMTC +#include +#include +#endif /* SMTC */ /* * For the fast tlb miss handlers, we keep a per cpu array of pointers @@ -54,6 +58,14 @@ extern unsigned long pgd_current[]; #define ASID_INC 0x1 #define ASID_MASK 0xfff +/* SMTC/34K debug hack - but maybe we'll keep it */ +#elif defined(CONFIG_MIPS_MT_SMTC) + +#define ASID_INC 0x1 +extern unsigned long smtc_asid_mask; +#define ASID_MASK (smtc_asid_mask) +#define HW_ASID_MASK 0xff +/* End SMTC/34K debug hack */ #else /* FIXME: not correct for R6000 */ #define ASID_INC 0x1 @@ -76,6 +88,8 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) #define ASID_VERSION_MASK ((unsigned long)~(ASID_MASK|(ASID_MASK-1))) #define ASID_FIRST_VERSION ((unsigned long)(~ASID_VERSION_MASK) + 1) +#ifndef CONFIG_MIPS_MT_SMTC +/* Normal, classic MIPS get_new_mmu_context */ static inline void get_new_mmu_context(struct mm_struct *mm, unsigned long cpu) { @@ -91,6 +105,12 @@ get_new_mmu_context(struct mm_struct *mm, unsigned long cpu) cpu_context(cpu, mm) = asid_cache(cpu) = asid; } +#else /* CONFIG_MIPS_MT_SMTC */ + +#define get_new_mmu_context(mm,cpu) smtc_get_new_mmu_context((mm),(cpu)) + +#endif /* CONFIG_MIPS_MT_SMTC */ + /* * Initialize the context related info for a new mm_struct * instance. @@ -111,14 +131,46 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, { unsigned int cpu = smp_processor_id(); unsigned long flags; - +#ifdef CONFIG_MIPS_MT_SMTC + unsigned long oldasid; + unsigned long mtflags; + int mytlb = (smtc_status & SMTC_TLB_SHARED) ? 0 : cpu_data[cpu].vpe_id; local_irq_save(flags); + mtflags = dvpe(); +#else /* Not SMTC */ + local_irq_save(flags); +#endif /* CONFIG_MIPS_MT_SMTC */ /* Check if our ASID is of an older version and thus invalid */ if ((cpu_context(cpu, next) ^ asid_cache(cpu)) & ASID_VERSION_MASK) get_new_mmu_context(next, cpu); - +#ifdef CONFIG_MIPS_MT_SMTC + /* + * If the EntryHi ASID being replaced happens to be + * the value flagged at ASID recycling time as having + * an extended life, clear the bit showing it being + * in use by this "CPU", and if that's the last bit, + * free up the ASID value for use and flush any old + * instances of it from the TLB. + */ + oldasid = (read_c0_entryhi() & ASID_MASK); + if(smtc_live_asid[mytlb][oldasid]) { + smtc_live_asid[mytlb][oldasid] &= ~(0x1 << cpu); + if(smtc_live_asid[mytlb][oldasid] == 0) + smtc_flush_tlb_asid(oldasid); + } + /* + * Tread softly on EntryHi, and so long as we support + * having ASID_MASK smaller than the hardware maximum, + * make sure no "soft" bits become "hard"... + */ + write_c0_entryhi((read_c0_entryhi() & ~HW_ASID_MASK) + | (cpu_context(cpu, next) & ASID_MASK)); + ehb(); /* Make sure it propagates to TCStatus */ + evpe(mtflags); +#else write_c0_entryhi(cpu_context(cpu, next)); +#endif /* CONFIG_MIPS_MT_SMTC */ TLBMISS_HANDLER_SETUP_PGD(next->pgd); /* @@ -151,12 +203,34 @@ activate_mm(struct mm_struct *prev, struct mm_struct *next) unsigned long flags; unsigned int cpu = smp_processor_id(); +#ifdef CONFIG_MIPS_MT_SMTC + unsigned long oldasid; + unsigned long mtflags; + int mytlb = (smtc_status & SMTC_TLB_SHARED) ? 0 : cpu_data[cpu].vpe_id; +#endif /* CONFIG_MIPS_MT_SMTC */ + local_irq_save(flags); /* Unconditionally get a new ASID. */ get_new_mmu_context(next, cpu); +#ifdef CONFIG_MIPS_MT_SMTC + /* See comments for similar code above */ + mtflags = dvpe(); + oldasid = read_c0_entryhi() & ASID_MASK; + if(smtc_live_asid[mytlb][oldasid]) { + smtc_live_asid[mytlb][oldasid] &= ~(0x1 << cpu); + if(smtc_live_asid[mytlb][oldasid] == 0) + smtc_flush_tlb_asid(oldasid); + } + /* See comments for similar code above */ + write_c0_entryhi((read_c0_entryhi() & ~HW_ASID_MASK) | + (cpu_context(cpu, next) & ASID_MASK)); + ehb(); /* Make sure it propagates to TCStatus */ + evpe(mtflags); +#else write_c0_entryhi(cpu_context(cpu, next)); +#endif /* CONFIG_MIPS_MT_SMTC */ TLBMISS_HANDLER_SETUP_PGD(next->pgd); /* mark mmu ownership change */ @@ -174,17 +248,49 @@ static inline void drop_mmu_context(struct mm_struct *mm, unsigned cpu) { unsigned long flags; +#ifdef CONFIG_MIPS_MT_SMTC + unsigned long oldasid; + /* Can't use spinlock because called from TLB flush within DVPE */ + unsigned int prevvpe; + int mytlb = (smtc_status & SMTC_TLB_SHARED) ? 0 : cpu_data[cpu].vpe_id; +#endif /* CONFIG_MIPS_MT_SMTC */ local_irq_save(flags); if (cpu_isset(cpu, mm->cpu_vm_mask)) { get_new_mmu_context(mm, cpu); +#ifdef CONFIG_MIPS_MT_SMTC + /* See comments for similar code above */ + prevvpe = dvpe(); + oldasid = (read_c0_entryhi() & ASID_MASK); + if(smtc_live_asid[mytlb][oldasid]) { + smtc_live_asid[mytlb][oldasid] &= ~(0x1 << cpu); + if(smtc_live_asid[mytlb][oldasid] == 0) + smtc_flush_tlb_asid(oldasid); + } + /* See comments for similar code above */ + write_c0_entryhi((read_c0_entryhi() & ~HW_ASID_MASK) + | cpu_asid(cpu, mm)); + ehb(); /* Make sure it propagates to TCStatus */ + evpe(prevvpe); +#else /* not CONFIG_MIPS_MT_SMTC */ write_c0_entryhi(cpu_asid(cpu, mm)); +#endif /* CONFIG_MIPS_MT_SMTC */ } else { /* will get a new context next time */ +#ifndef CONFIG_MIPS_MT_SMTC cpu_context(cpu, mm) = 0; +#else /* SMTC */ + int i; + + /* SMTC shares the TLB (and ASIDs) across VPEs */ + for (i = 0; i < num_online_cpus(); i++) { + if((smtc_status & SMTC_TLB_SHARED) + || (cpu_data[i].vpe_id == cpu_data[cpu].vpe_id)) + cpu_context(i, mm) = 0; + } +#endif /* CONFIG_MIPS_MT_SMTC */ } - local_irq_restore(flags); } diff --git a/include/asm-mips/processor.h b/include/asm-mips/processor.h index 39d2bd50fece..786651340de1 100644 --- a/include/asm-mips/processor.h +++ b/include/asm-mips/processor.h @@ -12,6 +12,7 @@ #define _ASM_PROCESSOR_H #include +#include #include #include @@ -107,6 +108,10 @@ struct mips_dsp_state { #define INIT_DSP {{0,},} +#define INIT_CPUMASK { \ + {0,} \ +} + typedef struct { unsigned long seg; } mm_segment_t; @@ -142,6 +147,7 @@ struct thread_struct { #define MF_LOGADE 2 /* Log address errors to syslog */ #define MF_32BIT_REGS 4 /* also implies 16/32 fprs */ #define MF_32BIT_ADDR 8 /* 32-bit address space (o32/n32) */ +#define MF_FPUBOUND 0x10 /* thread bound to FPU-full CPU set */ unsigned long mflags; unsigned long irix_trampoline; /* Wheee... */ unsigned long irix_oldctx; diff --git a/include/asm-mips/ptrace.h b/include/asm-mips/ptrace.h index 95c5839ac465..fa9d8713c12a 100644 --- a/include/asm-mips/ptrace.h +++ b/include/asm-mips/ptrace.h @@ -45,6 +45,10 @@ struct pt_regs { unsigned long cp0_badvaddr; unsigned long cp0_cause; unsigned long cp0_epc; +#ifdef CONFIG_MIPS_MT_SMTC + unsigned long cp0_tcstatus; + unsigned long smtc_pad; +#endif /* CONFIG_MIPS_MT_SMTC */ }; /* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ diff --git a/include/asm-mips/r4kcache.h b/include/asm-mips/r4kcache.h index 2f2eb95387f6..3c8e3c8d1a9a 100644 --- a/include/asm-mips/r4kcache.h +++ b/include/asm-mips/r4kcache.h @@ -15,6 +15,7 @@ #include #include #include +#include /* * This macro return a properly sign-extended address suitable as base address @@ -39,14 +40,118 @@ : \ : "i" (op), "R" (*(unsigned char *)(addr))) +#ifdef CONFIG_MIPS_MT +/* + * Temporary hacks for SMTC debug. Optionally force single-threaded + * execution during I-cache flushes. + */ + +#define PROTECT_CACHE_FLUSHES 1 + +#ifdef PROTECT_CACHE_FLUSHES + +extern int mt_protiflush; +extern int mt_protdflush; +extern void mt_cflush_lockdown(void); +extern void mt_cflush_release(void); + +#define BEGIN_MT_IPROT \ + unsigned long flags = 0; \ + unsigned long mtflags = 0; \ + if(mt_protiflush) { \ + local_irq_save(flags); \ + ehb(); \ + mtflags = dvpe(); \ + mt_cflush_lockdown(); \ + } + +#define END_MT_IPROT \ + if(mt_protiflush) { \ + mt_cflush_release(); \ + evpe(mtflags); \ + local_irq_restore(flags); \ + } + +#define BEGIN_MT_DPROT \ + unsigned long flags = 0; \ + unsigned long mtflags = 0; \ + if(mt_protdflush) { \ + local_irq_save(flags); \ + ehb(); \ + mtflags = dvpe(); \ + mt_cflush_lockdown(); \ + } + +#define END_MT_DPROT \ + if(mt_protdflush) { \ + mt_cflush_release(); \ + evpe(mtflags); \ + local_irq_restore(flags); \ + } + +#else + +#define BEGIN_MT_IPROT +#define BEGIN_MT_DPROT +#define END_MT_IPROT +#define END_MT_DPROT + +#endif /* PROTECT_CACHE_FLUSHES */ + +#define __iflush_prologue \ + unsigned long redundance; \ + extern int mt_n_iflushes; \ + BEGIN_MT_IPROT \ + for (redundance = 0; redundance < mt_n_iflushes; redundance++) { + +#define __iflush_epilogue \ + END_MT_IPROT \ + } + +#define __dflush_prologue \ + unsigned long redundance; \ + extern int mt_n_dflushes; \ + BEGIN_MT_DPROT \ + for (redundance = 0; redundance < mt_n_dflushes; redundance++) { + +#define __dflush_epilogue \ + END_MT_DPROT \ + } + +#define __inv_dflush_prologue __dflush_prologue +#define __inv_dflush_epilogue __dflush_epilogue +#define __sflush_prologue { +#define __sflush_epilogue } +#define __inv_sflush_prologue __sflush_prologue +#define __inv_sflush_epilogue __sflush_epilogue + +#else /* CONFIG_MIPS_MT */ + +#define __iflush_prologue { +#define __iflush_epilogue } +#define __dflush_prologue { +#define __dflush_epilogue } +#define __inv_dflush_prologue { +#define __inv_dflush_epilogue } +#define __sflush_prologue { +#define __sflush_epilogue } +#define __inv_sflush_prologue { +#define __inv_sflush_epilogue } + +#endif /* CONFIG_MIPS_MT */ + static inline void flush_icache_line_indexed(unsigned long addr) { + __iflush_prologue cache_op(Index_Invalidate_I, addr); + __iflush_epilogue } static inline void flush_dcache_line_indexed(unsigned long addr) { + __dflush_prologue cache_op(Index_Writeback_Inv_D, addr); + __dflush_epilogue } static inline void flush_scache_line_indexed(unsigned long addr) @@ -56,17 +161,23 @@ static inline void flush_scache_line_indexed(unsigned long addr) static inline void flush_icache_line(unsigned long addr) { + __iflush_prologue cache_op(Hit_Invalidate_I, addr); + __iflush_epilogue } static inline void flush_dcache_line(unsigned long addr) { + __dflush_prologue cache_op(Hit_Writeback_Inv_D, addr); + __dflush_epilogue } static inline void invalidate_dcache_line(unsigned long addr) { + __dflush_prologue cache_op(Hit_Invalidate_D, addr); + __dflush_epilogue } static inline void invalidate_scache_line(unsigned long addr) @@ -239,9 +350,13 @@ static inline void blast_##pfx##cache##lsize(void) \ current_cpu_data.desc.waybit; \ unsigned long ws, addr; \ \ + __##pfx##flush_prologue \ + \ for (ws = 0; ws < ws_end; ws += ws_inc) \ for (addr = start; addr < end; addr += lsize * 32) \ cache##lsize##_unroll32(addr|ws,indexop); \ + \ + __##pfx##flush_epilogue \ } \ \ static inline void blast_##pfx##cache##lsize##_page(unsigned long page) \ @@ -249,10 +364,14 @@ static inline void blast_##pfx##cache##lsize##_page(unsigned long page) \ unsigned long start = page; \ unsigned long end = page + PAGE_SIZE; \ \ + __##pfx##flush_prologue \ + \ do { \ cache##lsize##_unroll32(start,hitop); \ start += lsize * 32; \ } while (start < end); \ + \ + __##pfx##flush_epilogue \ } \ \ static inline void blast_##pfx##cache##lsize##_page_indexed(unsigned long page) \ @@ -265,9 +384,13 @@ static inline void blast_##pfx##cache##lsize##_page_indexed(unsigned long page) current_cpu_data.desc.waybit; \ unsigned long ws, addr; \ \ + __##pfx##flush_prologue \ + \ for (ws = 0; ws < ws_end; ws += ws_inc) \ for (addr = start; addr < end; addr += lsize * 32) \ cache##lsize##_unroll32(addr|ws,indexop); \ + \ + __##pfx##flush_epilogue \ } __BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16) @@ -288,12 +411,17 @@ static inline void prot##blast_##pfx##cache##_range(unsigned long start, \ unsigned long lsize = cpu_##desc##_line_size(); \ unsigned long addr = start & ~(lsize - 1); \ unsigned long aend = (end - 1) & ~(lsize - 1); \ + \ + __##pfx##flush_prologue \ + \ while (1) { \ prot##cache_op(hitop, addr); \ if (addr == aend) \ break; \ addr += lsize; \ } \ + \ + __##pfx##flush_epilogue \ } __BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_) diff --git a/include/asm-mips/smtc.h b/include/asm-mips/smtc.h new file mode 100644 index 000000000000..e1941d1b8726 --- /dev/null +++ b/include/asm-mips/smtc.h @@ -0,0 +1,55 @@ +#ifndef _ASM_SMTC_MT_H +#define _ASM_SMTC_MT_H + +/* + * Definitions for SMTC multitasking on MIPS MT cores + */ + +#include + +/* + * System-wide SMTC status information + */ + +extern unsigned int smtc_status; + +#define SMTC_TLB_SHARED 0x00000001 +#define SMTC_MTC_ACTIVE 0x00000002 + +/* + * TLB/ASID Management information + */ + +#define MAX_SMTC_TLBS 2 +#define MAX_SMTC_ASIDS 256 +#if NR_CPUS <= 8 +typedef char asiduse; +#else +#if NR_CPUS <= 16 +typedef short asiduse; +#else +typedef long asiduse; +#endif +#endif + +extern asiduse smtc_live_asid[MAX_SMTC_TLBS][MAX_SMTC_ASIDS]; + +void smtc_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu); + +void smtc_flush_tlb_asid(unsigned long asid); +extern int mipsmt_build_cpu_map(int startslot); +extern void mipsmt_prepare_cpus(void); +extern void smtc_smp_finish(void); +extern void smtc_boot_secondary(int cpu, struct task_struct *t); + +/* + * Sharing the TLB between multiple VPEs means that the + * "random" index selection function is not allowed to + * select the current value of the Index register. To + * avoid additional TLB pressure, the Index registers + * are "parked" with an non-Valid value. + */ + +#define PARKED_INDEX ((unsigned int)0x80000000) + +#endif /* _ASM_SMTC_MT_H */ diff --git a/include/asm-mips/smtc_ipi.h b/include/asm-mips/smtc_ipi.h new file mode 100644 index 000000000000..f22c3e2f993a --- /dev/null +++ b/include/asm-mips/smtc_ipi.h @@ -0,0 +1,118 @@ +/* + * Definitions used in MIPS MT SMTC "Interprocessor Interrupt" code. + */ +#ifndef __ASM_SMTC_IPI_H +#define __ASM_SMTC_IPI_H + +//#define SMTC_IPI_DEBUG + +#ifdef SMTC_IPI_DEBUG +#include +#include +#endif /* SMTC_IPI_DEBUG */ + +/* + * An IPI "message" + */ + +struct smtc_ipi { + struct smtc_ipi *flink; + int type; + void *arg; + int dest; +#ifdef SMTC_IPI_DEBUG + int sender; + long stamp; +#endif /* SMTC_IPI_DEBUG */ +}; + +/* + * Defined IPI Types + */ + +#define LINUX_SMP_IPI 1 +#define SMTC_CLOCK_TICK 2 + +/* + * A queue of IPI messages + */ + +struct smtc_ipi_q { + struct smtc_ipi *head; + spinlock_t lock; + struct smtc_ipi *tail; + int depth; +}; + +extern struct smtc_ipi_q IPIQ[NR_CPUS]; +extern struct smtc_ipi_q freeIPIq; + +static inline void smtc_ipi_nq(struct smtc_ipi_q *q, struct smtc_ipi *p) +{ + long flags; + + spin_lock_irqsave(&q->lock, flags); + if (q->head == NULL) + q->head = q->tail = p; + else + q->tail->flink = p; + p->flink = NULL; + q->tail = p; + q->depth++; +#ifdef SMTC_IPI_DEBUG + p->sender = read_c0_tcbind(); + p->stamp = read_c0_count(); +#endif /* SMTC_IPI_DEBUG */ + spin_unlock_irqrestore(&q->lock, flags); +} + +static inline struct smtc_ipi *smtc_ipi_dq(struct smtc_ipi_q *q) +{ + struct smtc_ipi *p; + long flags; + + spin_lock_irqsave(&q->lock, flags); + if (q->head == NULL) + p = NULL; + else { + p = q->head; + q->head = q->head->flink; + q->depth--; + /* Arguably unnecessary, but leaves queue cleaner */ + if (q->head == NULL) + q->tail = NULL; + } + spin_unlock_irqrestore(&q->lock, flags); + return p; +} + +static inline void smtc_ipi_req(struct smtc_ipi_q *q, struct smtc_ipi *p) +{ + long flags; + + spin_lock_irqsave(&q->lock, flags); + if (q->head == NULL) { + q->head = q->tail = p; + p->flink = NULL; + } else { + p->flink = q->head; + q->head = p; + } + q->depth++; + spin_unlock_irqrestore(&q->lock, flags); +} + +static inline int smtc_ipi_qdepth(struct smtc_ipi_q *q) +{ + long flags; + int retval; + + spin_lock_irqsave(&q->lock, flags); + retval = q->depth; + spin_unlock_irqrestore(&q->lock, flags); + return retval; +} + +extern void smtc_send_ipi(int cpu, int type, unsigned int action); + +#endif /* __ASM_SMTC_IPI_H */ diff --git a/include/asm-mips/smtc_proc.h b/include/asm-mips/smtc_proc.h new file mode 100644 index 000000000000..25da651f1f5f --- /dev/null +++ b/include/asm-mips/smtc_proc.h @@ -0,0 +1,23 @@ +/* + * Definitions for SMTC /proc entries + * Copyright(C) 2005 MIPS Technologies Inc. + */ +#ifndef __ASM_SMTC_PROC_H +#define __ASM_SMTC_PROC_H + +/* + * per-"CPU" statistics + */ + +struct smtc_cpu_proc { + unsigned long timerints; + unsigned long selfipis; +}; + +extern struct smtc_cpu_proc smtc_cpu_stats[NR_CPUS]; + +/* Count of number of recoveries of "stolen" FPU access rights on 34K */ + +extern atomic_t smtc_fpu_recoveries; + +#endif /* __ASM_SMTC_PROC_H */ diff --git a/include/asm-mips/stackframe.h b/include/asm-mips/stackframe.h index 2acf3e844f00..c4856a874965 100644 --- a/include/asm-mips/stackframe.h +++ b/include/asm-mips/stackframe.h @@ -14,9 +14,14 @@ #include #include +#include #include #include +#ifdef CONFIG_MIPS_MT_SMTC +#include +#endif /* CONFIG_MIPS_MT_SMTC */ + .macro SAVE_AT .set push .set noat @@ -57,13 +62,30 @@ #ifdef CONFIG_SMP .macro get_saved_sp /* SMP variation */ #ifdef CONFIG_32BIT +#ifdef CONFIG_MIPS_MT_SMTC + .set mips32 + mfc0 k0, CP0_TCBIND; + .set mips0 + lui k1, %hi(kernelsp) + srl k0, k0, 19 + /* No need to shift down and up to clear bits 0-1 */ +#else mfc0 k0, CP0_CONTEXT lui k1, %hi(kernelsp) srl k0, k0, 23 +#endif addu k1, k0 LONG_L k1, %lo(kernelsp)(k1) #endif #ifdef CONFIG_64BIT +#ifdef CONFIG_MIPS_MT_SMTC + .set mips64 + mfc0 k0, CP0_TCBIND; + .set mips0 + lui k0, %highest(kernelsp) + dsrl k1, 19 + /* No need to shift down and up to clear bits 0-2 */ +#else MFC0 k1, CP0_CONTEXT lui k0, %highest(kernelsp) dsrl k1, 23 @@ -71,19 +93,30 @@ dsll k0, k0, 16 daddiu k0, %hi(kernelsp) dsll k0, k0, 16 +#endif /* CONFIG_MIPS_MT_SMTC */ daddu k1, k1, k0 LONG_L k1, %lo(kernelsp)(k1) -#endif +#endif /* CONFIG_64BIT */ .endm .macro set_saved_sp stackp temp temp2 #ifdef CONFIG_32BIT +#ifdef CONFIG_MIPS_MT_SMTC + mfc0 \temp, CP0_TCBIND + srl \temp, 19 +#else mfc0 \temp, CP0_CONTEXT srl \temp, 23 #endif +#endif #ifdef CONFIG_64BIT +#ifdef CONFIG_MIPS_MT_SMTC + mfc0 \temp, CP0_TCBIND + dsrl \temp, 19 +#else MFC0 \temp, CP0_CONTEXT dsrl \temp, 23 +#endif #endif LONG_S \stackp, kernelsp(\temp) .endm @@ -122,10 +155,25 @@ PTR_SUBU sp, k1, PT_SIZE LONG_S k0, PT_R29(sp) LONG_S $3, PT_R3(sp) + /* + * You might think that you don't need to save $0, + * but the FPU emulator and gdb remote debug stub + * need it to operate correctly + */ LONG_S $0, PT_R0(sp) mfc0 v1, CP0_STATUS LONG_S $2, PT_R2(sp) LONG_S v1, PT_STATUS(sp) +#ifdef CONFIG_MIPS_MT_SMTC + /* + * Ideally, these instructions would be shuffled in + * to cover the pipeline delay. + */ + .set mips32 + mfc0 v1, CP0_TCSTATUS + .set mips0 + LONG_S v1, PT_TCSTATUS(sp) +#endif /* CONFIG_MIPS_MT_SMTC */ LONG_S $4, PT_R4(sp) mfc0 v1, CP0_CAUSE LONG_S $5, PT_R5(sp) @@ -234,14 +282,36 @@ .endm #else +/* + * For SMTC kernel, global IE should be left set, and interrupts + * controlled exclusively via IXMT. + */ +#ifdef CONFIG_MIPS_MT_SMTC +#define STATMASK 0x1e +#else +#define STATMASK 0x1f +#endif .macro RESTORE_SOME .set push .set reorder .set noat +#ifdef CONFIG_MIPS_MT_SMTC + .set mips32r2 + /* + * This may not really be necessary if ints are already + * inhibited here. + */ + mfc0 v0, CP0_TCSTATUS + ori v0, TCSTATUS_IXMT + mtc0 v0, CP0_TCSTATUS + ehb + DMT 5 # dmt a1 + jal mips_ihb +#endif /* CONFIG_MIPS_MT_SMTC */ mfc0 a0, CP0_STATUS - ori a0, 0x1f - xori a0, 0x1f + ori a0, STATMASK + xori a0, STATMASK mtc0 a0, CP0_STATUS li v1, 0xff00 and a0, v1 @@ -250,6 +320,26 @@ and v0, v1 or v0, a0 mtc0 v0, CP0_STATUS +#ifdef CONFIG_MIPS_MT_SMTC +/* + * Only after EXL/ERL have been restored to status can we + * restore TCStatus.IXMT. + */ + LONG_L v1, PT_TCSTATUS(sp) + ehb + mfc0 v0, CP0_TCSTATUS + andi v1, TCSTATUS_IXMT + /* We know that TCStatua.IXMT should be set from above */ + xori v0, v0, TCSTATUS_IXMT + or v0, v0, v1 + mtc0 v0, CP0_TCSTATUS + ehb + andi a1, a1, VPECONTROL_TE + beqz a1, 1f + emt +1: + .set mips0 +#endif /* CONFIG_MIPS_MT_SMTC */ LONG_L v1, PT_EPC(sp) MTC0 v1, CP0_EPC LONG_L $31, PT_R31(sp) @@ -302,11 +392,33 @@ * Set cp0 enable bit as sign that we're running on the kernel stack */ .macro CLI +#if !defined(CONFIG_MIPS_MT_SMTC) mfc0 t0, CP0_STATUS li t1, ST0_CU0 | 0x1f or t0, t1 xori t0, 0x1f mtc0 t0, CP0_STATUS +#else /* CONFIG_MIPS_MT_SMTC */ + /* + * For SMTC, we need to set privilege + * and disable interrupts only for the + * current TC, using the TCStatus register. + */ + mfc0 t0,CP0_TCSTATUS + /* Fortunately CU 0 is in the same place in both registers */ + /* Set TCU0, TMX, TKSU (for later inversion) and IXMT */ + li t1, ST0_CU0 | 0x08001c00 + or t0,t1 + /* Clear TKSU, leave IXMT */ + xori t0, 0x00001800 + mtc0 t0, CP0_TCSTATUS + ehb + /* We need to leave the global IE bit set, but clear EXL...*/ + mfc0 t0, CP0_STATUS + ori t0, ST0_EXL | ST0_ERL + xori t0, ST0_EXL | ST0_ERL + mtc0 t0, CP0_STATUS +#endif /* CONFIG_MIPS_MT_SMTC */ irq_disable_hazard .endm @@ -315,11 +427,35 @@ * Set cp0 enable bit as sign that we're running on the kernel stack */ .macro STI +#if !defined(CONFIG_MIPS_MT_SMTC) mfc0 t0, CP0_STATUS li t1, ST0_CU0 | 0x1f or t0, t1 xori t0, 0x1e mtc0 t0, CP0_STATUS +#else /* CONFIG_MIPS_MT_SMTC */ + /* + * For SMTC, we need to set privilege + * and enable interrupts only for the + * current TC, using the TCStatus register. + */ + ehb + mfc0 t0,CP0_TCSTATUS + /* Fortunately CU 0 is in the same place in both registers */ + /* Set TCU0, TKSU (for later inversion) and IXMT */ + li t1, ST0_CU0 | 0x08001c00 + or t0,t1 + /* Clear TKSU *and* IXMT */ + xori t0, 0x00001c00 + mtc0 t0, CP0_TCSTATUS + ehb + /* We need to leave the global IE bit set, but clear EXL...*/ + mfc0 t0, CP0_STATUS + ori t0, ST0_EXL + xori t0, ST0_EXL + mtc0 t0, CP0_STATUS + /* irq_enable_hazard below should expand to EHB for 24K/34K cpus */ +#endif /* CONFIG_MIPS_MT_SMTC */ irq_enable_hazard .endm @@ -328,11 +464,56 @@ * Set cp0 enable bit as sign that we're running on the kernel stack */ .macro KMODE +#ifdef CONFIG_MIPS_MT_SMTC + /* + * This gets baroque in SMTC. We want to + * protect the non-atomic clearing of EXL + * with DMT/EMT, but we don't want to take + * an interrupt while DMT is still in effect. + */ + + /* KMODE gets invoked from both reorder and noreorder code */ + .set push + .set mips32r2 + .set noreorder + mfc0 v0, CP0_TCSTATUS + andi v1, v0, TCSTATUS_IXMT + ori v0, TCSTATUS_IXMT + mtc0 v0, CP0_TCSTATUS + ehb + DMT 2 # dmt v0 + /* + * We don't know a priori if ra is "live" + */ + move t0, ra + jal mips_ihb + nop /* delay slot */ + move ra, t0 +#endif /* CONFIG_MIPS_MT_SMTC */ mfc0 t0, CP0_STATUS li t1, ST0_CU0 | 0x1e or t0, t1 xori t0, 0x1e mtc0 t0, CP0_STATUS +#ifdef CONFIG_MIPS_MT_SMTC + ehb + andi v0, v0, VPECONTROL_TE + beqz v0, 2f + nop /* delay slot */ + emt +2: + mfc0 v0, CP0_TCSTATUS + /* Clear IXMT, then OR in previous value */ + ori v0, TCSTATUS_IXMT + xori v0, TCSTATUS_IXMT + or v0, v1, v0 + mtc0 v0, CP0_TCSTATUS + /* + * irq_disable_hazard below should expand to EHB + * on 24K/34K CPUS + */ + .set pop +#endif /* CONFIG_MIPS_MT_SMTC */ irq_disable_hazard .endm -- cgit v1.2.3 From f088fc84f94c1a36943e28ad704a9a740a35f877 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 5 Apr 2006 09:45:47 +0100 Subject: [MIPS] FPU affinity for MT ASE. Signed-off-by: Ralf Baechle --- arch/mips/Kconfig | 5 +++++ arch/mips/kernel/process.c | 11 +++++++++++ arch/mips/kernel/scall32-o32.S | 11 +++++++++++ arch/mips/kernel/setup.c | 5 ++++- arch/mips/kernel/smp-mt.c | 11 +++++++++++ arch/mips/kernel/traps.c | 30 ++++++++++++++++++++++++++++++ include/asm-mips/cpu-features.h | 2 +- include/asm-mips/fpu.h | 4 ++++ include/asm-mips/processor.h | 16 ++++++++++++++++ include/asm-mips/system.h | 32 ++++++++++++++++++++++++++++++++ 10 files changed, 125 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index f9be549645ea..87f0b79c6b15 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1464,6 +1464,11 @@ config MIPS_VPE_LOADER endchoice +config MIPS_MT_FPAFF + bool "Dynamic FPU affinity for FP-intensive threads" + depends on MIPS_MT + default y + config MIPS_VPE_LOADER_TOM bool "Load VPE program into memory hidden from linux" depends on MIPS_VPE_LOADER diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 8b393df460a2..199a06e873c6 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -185,6 +185,17 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, childregs->cp0_status &= ~(ST0_CU2|ST0_CU1); clear_tsk_thread_flag(p, TIF_USEDFPU); +#ifdef CONFIG_MIPS_MT_FPAFF + /* + * FPU affinity support is cleaner if we track the + * user-visible CPU affinity from the very beginning. + * The generic cpus_allowed mask will already have + * been copied from the parent before copy_thread + * is invoked. + */ + p->thread.user_cpus_allowed = p->cpus_allowed; +#endif /* CONFIG_MIPS_MT_FPAFF */ + if (clone_flags & CLONE_SETTLS) ti->tp_value = regs->regs[7]; diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S index 4e36b87be1ed..a0ac0e5f61ad 100644 --- a/arch/mips/kernel/scall32-o32.S +++ b/arch/mips/kernel/scall32-o32.S @@ -569,8 +569,19 @@ einval: li v0, -EINVAL sys sys_tkill 2 sys sys_sendfile64 5 sys sys_futex 6 +#ifdef CONFIG_MIPS_MT_FPAFF + /* + * For FPU affinity scheduling on MIPS MT processors, we need to + * intercept sys_sched_xxxaffinity() calls until we get a proper hook + * in kernel/sched.c. Considered only temporary we only support these + * hooks for the 32-bit kernel - there is no MIPS64 MT processor atm. + */ + sys mipsmt_sys_sched_setaffinity 3 + sys mipsmt_sys_sched_getaffinity 3 +#else sys sys_sched_setaffinity 3 sys sys_sched_getaffinity 3 /* 4240 */ +#endif /* CONFIG_MIPS_MT_FPAFF */ sys sys_io_setup 2 sys sys_io_destroy 1 sys sys_io_getevents 5 diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index dcbfd27071f0..bcf1b10e518f 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -529,7 +529,10 @@ void __init setup_arch(char **cmdline_p) int __init fpu_disable(char *s) { - cpu_data[0].options &= ~MIPS_CPU_FPU; + int i; + + for (i = 0; i < NR_CPUS; i++) + cpu_data[i].options &= ~MIPS_CPU_FPU; return 1; } diff --git a/arch/mips/kernel/smp-mt.c b/arch/mips/kernel/smp-mt.c index 19b8e4b31b79..57770902b9ae 100644 --- a/arch/mips/kernel/smp-mt.c +++ b/arch/mips/kernel/smp-mt.c @@ -150,6 +150,11 @@ void plat_smp_setup(void) unsigned long val; int i, num; +#ifdef CONFIG_MIPS_MT_FPAFF + /* If we have an FPU, enroll ourselves in the FPU-full mask */ + if (cpu_has_fpu) + cpu_set(0, mt_fpu_cpumask); +#endif /* CONFIG_MIPS_MT_FPAFF */ if (!cpu_has_mipsmt) return; @@ -312,6 +317,12 @@ void prom_smp_finish(void) { write_c0_compare(read_c0_count() + (8* mips_hpt_frequency/HZ)); +#ifdef CONFIG_MIPS_MT_FPAFF + /* If we have an FPU, enroll ourselves in the FPU-full mask */ + if (cpu_has_fpu) + cpu_set(smp_processor_id(), mt_fpu_cpumask); +#endif /* CONFIG_MIPS_MT_FPAFF */ + local_irq_enable(); } diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 6336fe8008ec..e9902d89dc0a 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -758,6 +758,36 @@ asmlinkage void do_cpu(struct pt_regs *regs) ¤t->thread.fpu.soft); if (sig) force_sig(sig, current); +#ifdef CONFIG_MIPS_MT_FPAFF + else { + /* + * MIPS MT processors may have fewer FPU contexts + * than CPU threads. If we've emulated more than + * some threshold number of instructions, force + * migration to a "CPU" that has FP support. + */ + if(mt_fpemul_threshold > 0 + && ((current->thread.emulated_fp++ + > mt_fpemul_threshold))) { + /* + * If there's no FPU present, or if the + * application has already restricted + * the allowed set to exclude any CPUs + * with FPUs, we'll skip the procedure. + */ + if (cpus_intersects(current->cpus_allowed, + mt_fpu_cpumask)) { + cpumask_t tmask; + + cpus_and(tmask, + current->thread.user_cpus_allowed, + mt_fpu_cpumask); + set_cpus_allowed(current, tmask); + current->thread.mflags |= MF_FPUBOUND; + } + } + } +#endif /* CONFIG_MIPS_MT_FPAFF */ } return; diff --git a/include/asm-mips/cpu-features.h b/include/asm-mips/cpu-features.h index 3f2b6d9ac45e..254e11ed247b 100644 --- a/include/asm-mips/cpu-features.h +++ b/include/asm-mips/cpu-features.h @@ -40,7 +40,7 @@ #define cpu_has_sb1_cache (cpu_data[0].options & MIPS_CPU_SB1_CACHE) #endif #ifndef cpu_has_fpu -#define cpu_has_fpu (cpu_data[0].options & MIPS_CPU_FPU) +#define cpu_has_fpu (current_cpu_data.options & MIPS_CPU_FPU) #endif #ifndef cpu_has_32fpr #define cpu_has_32fpr (cpu_data[0].options & MIPS_CPU_32FPR) diff --git a/include/asm-mips/fpu.h b/include/asm-mips/fpu.h index 9c828b1f8218..b0f50015e252 100644 --- a/include/asm-mips/fpu.h +++ b/include/asm-mips/fpu.h @@ -21,6 +21,10 @@ #include #include +#ifdef CONFIG_MIPS_MT_FPAFF +#include +#endif + struct sigcontext; struct sigcontext32; diff --git a/include/asm-mips/processor.h b/include/asm-mips/processor.h index 786651340de1..0fb75f0762e0 100644 --- a/include/asm-mips/processor.h +++ b/include/asm-mips/processor.h @@ -134,6 +134,12 @@ struct thread_struct { /* Saved fpu/fpu emulator stuff. */ union mips_fpu_union fpu; +#ifdef CONFIG_MIPS_MT_FPAFF + /* Emulated instruction count */ + unsigned long emulated_fp; + /* Saved per-thread scheduler affinity mask */ + cpumask_t user_cpus_allowed; +#endif /* CONFIG_MIPS_MT_FPAFF */ /* Saved state of the DSP ASE, if available. */ struct mips_dsp_state dsp; @@ -159,6 +165,12 @@ struct thread_struct { #define MF_N32 MF_32BIT_ADDR #define MF_N64 0 +#ifdef CONFIG_MIPS_MT_FPAFF +#define FPAFF_INIT 0, INIT_CPUMASK, +#else +#define FPAFF_INIT +#endif /* CONFIG_MIPS_MT_FPAFF */ + #define INIT_THREAD { \ /* \ * saved main processor registers \ @@ -173,6 +185,10 @@ struct thread_struct { * saved fpu/fpu emulator stuff \ */ \ INIT_FPU, \ + /* \ + * fpu affinity state (null if not FPAFF) \ + */ \ + FPAFF_INIT \ /* \ * saved dsp/dsp emulator stuff \ */ \ diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h index 39026690d9e4..261f71d16a07 100644 --- a/include/asm-mips/system.h +++ b/include/asm-mips/system.h @@ -155,6 +155,37 @@ extern asmlinkage void *resume(void *last, void *next, void *next_ti); struct task_struct; +#ifdef CONFIG_MIPS_MT_FPAFF + +/* + * Handle the scheduler resume end of FPU affinity management. We do this + * inline to try to keep the overhead down. If we have been forced to run on + * a "CPU" with an FPU because of a previous high level of FP computation, + * but did not actually use the FPU during the most recent time-slice (CU1 + * isn't set), we undo the restriction on cpus_allowed. + * + * We're not calling set_cpus_allowed() here, because we have no need to + * force prompt migration - we're already switching the current CPU to a + * different thread. + */ + +#define switch_to(prev,next,last) \ +do { \ + if (cpu_has_fpu && \ + (prev->thread.mflags & MF_FPUBOUND) && \ + (!(KSTK_STATUS(prev) & ST0_CU1))) { \ + prev->thread.mflags &= ~MF_FPUBOUND; \ + prev->cpus_allowed = prev->thread.user_cpus_allowed; \ + } \ + if (cpu_has_dsp) \ + __save_dsp(prev); \ + next->thread.emulated_fp = 0; \ + (last) = resume(prev, next, next->thread_info); \ + if (cpu_has_dsp) \ + __restore_dsp(current); \ +} while(0) + +#else #define switch_to(prev,next,last) \ do { \ if (cpu_has_dsp) \ @@ -163,6 +194,7 @@ do { \ if (cpu_has_dsp) \ __restore_dsp(current); \ } while(0) +#endif /* * On SMP systems, when the scheduler does migration-cost autodetection, -- cgit v1.2.3 From b4ade4bf8811c7267b9f32b4a5d8fcfde714adac Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 5 Apr 2006 09:45:48 +0100 Subject: [MIPS] MIPS boards: Set HZ to 100. 1000Hz will bring an FPGA CPU down on it's knees and it's even worse on multithreaded cores. Signed-off-by: Ralf Baechle --- include/asm-mips/mach-mips/param.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 include/asm-mips/mach-mips/param.h (limited to 'include') diff --git a/include/asm-mips/mach-mips/param.h b/include/asm-mips/mach-mips/param.h new file mode 100644 index 000000000000..805ef6d27d3c --- /dev/null +++ b/include/asm-mips/mach-mips/param.h @@ -0,0 +1,13 @@ +/* + * 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) 2003 by Ralf Baechle + */ +#ifndef __ASM_MACH_MIPS_PARAM_H +#define __ASM_MACH_MIPS_PARAM_H + +#define HZ 100 /* Internal kernel timer frequency */ + +#endif /* __ASM_MACH_MIPS_PARAM_H */ -- cgit v1.2.3 From 7e3bfc7cfc402458b0386086ab650ce811720927 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 5 Apr 2006 20:42:04 +0100 Subject: [MIPS] Handle IDE PIO cache aliases on SMP. Signed-off-by: Ralf Baechle --- arch/mips/mm/c-r3k.c | 5 ++++ arch/mips/mm/c-r4k.c | 1 + arch/mips/mm/c-sb1.c | 1 + arch/mips/mm/c-tx39.c | 7 ++++++ arch/mips/mm/cache.c | 1 + include/asm-mips/cacheflush.h | 1 + include/asm-mips/mach-generic/ide.h | 46 +++++++++++++++++++++++++++++++++++-- 7 files changed, 60 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/mips/mm/c-r3k.c b/arch/mips/mm/c-r3k.c index 9dd1352d5748..bb041a22f20a 100644 --- a/arch/mips/mm/c-r3k.c +++ b/arch/mips/mm/c-r3k.c @@ -260,6 +260,10 @@ static void r3k_flush_cache_page(struct vm_area_struct *vma, unsigned long page, { } +static void local_r3k_flush_data_cache_page(unsigned long addr) +{ +} + static void r3k_flush_data_cache_page(unsigned long addr) { } @@ -335,6 +339,7 @@ void __init r3k_cache_init(void) flush_icache_range = r3k_flush_icache_range; flush_cache_sigtramp = r3k_flush_cache_sigtramp; + local_flush_data_cache_page = local_r3k_flush_data_cache_page; flush_data_cache_page = r3k_flush_data_cache_page; _dma_cache_wback_inv = r3k_dma_cache_wback_inv; diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index c4c208449d87..d88c6686413a 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -1199,6 +1199,7 @@ void __init r4k_cache_init(void) flush_cache_sigtramp = r4k_flush_cache_sigtramp; flush_icache_all = r4k_flush_icache_all; + local_flush_data_cache_page = local_r4k_flush_data_cache_page; flush_data_cache_page = r4k_flush_data_cache_page; flush_icache_range = r4k_flush_icache_range; diff --git a/arch/mips/mm/c-sb1.c b/arch/mips/mm/c-sb1.c index 2f08b535f20e..f9b129491b1e 100644 --- a/arch/mips/mm/c-sb1.c +++ b/arch/mips/mm/c-sb1.c @@ -528,6 +528,7 @@ void sb1_cache_init(void) flush_cache_page = sb1_flush_cache_page; flush_cache_sigtramp = sb1_flush_cache_sigtramp; + local_flush_data_cache_page = (void *) sb1_nop; flush_data_cache_page = (void *) sb1_nop; /* Full flush */ diff --git a/arch/mips/mm/c-tx39.c b/arch/mips/mm/c-tx39.c index fe232e3988e3..5dfc9b1901f6 100644 --- a/arch/mips/mm/c-tx39.c +++ b/arch/mips/mm/c-tx39.c @@ -216,6 +216,11 @@ static void tx39_flush_cache_page(struct vm_area_struct *vma, unsigned long page tx39_blast_icache_page_indexed(page); } +static void local_tx39_flush_data_cache_page(void * addr) +{ + tx39_blast_dcache_page(addr); +} + static void tx39_flush_data_cache_page(unsigned long addr) { tx39_blast_dcache_page(addr); @@ -381,6 +386,7 @@ void __init tx39_cache_init(void) flush_icache_range = (void *) tx39h_flush_icache_all; flush_cache_sigtramp = (void *) tx39h_flush_icache_all; + local_flush_data_cache_page = (void *) tx39h_flush_icache_all; flush_data_cache_page = (void *) tx39h_flush_icache_all; _dma_cache_wback_inv = tx39h_dma_cache_wback_inv; @@ -406,6 +412,7 @@ void __init tx39_cache_init(void) flush_icache_range = tx39_flush_icache_range; flush_cache_sigtramp = tx39_flush_cache_sigtramp; + local_flush_data_cache_page = local_tx39_flush_data_cache_page; flush_data_cache_page = tx39_flush_data_cache_page; _dma_cache_wback_inv = tx39_dma_cache_wback_inv; diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c index 591c22b080e4..83a56296be86 100644 --- a/arch/mips/mm/cache.c +++ b/arch/mips/mm/cache.c @@ -30,6 +30,7 @@ void (*flush_icache_page)(struct vm_area_struct *vma, struct page *page); /* MIPS specific cache operations */ void (*flush_cache_sigtramp)(unsigned long addr); +void (*local_flush_data_cache_page)(void * addr); void (*flush_data_cache_page)(unsigned long addr); void (*flush_icache_all)(void); diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h index aeae9fabf4a9..47bc8f6c20d2 100644 --- a/include/asm-mips/cacheflush.h +++ b/include/asm-mips/cacheflush.h @@ -74,6 +74,7 @@ static inline void copy_from_user_page(struct vm_area_struct *vma, extern void (*flush_cache_sigtramp)(unsigned long addr); extern void (*flush_icache_all)(void); +extern void (*local_flush_data_cache_page)(void * addr); extern void (*flush_data_cache_page)(unsigned long addr); /* diff --git a/include/asm-mips/mach-generic/ide.h b/include/asm-mips/mach-generic/ide.h index 550979a9ea9d..e3315359500a 100644 --- a/include/asm-mips/mach-generic/ide.h +++ b/include/asm-mips/mach-generic/ide.h @@ -104,65 +104,107 @@ static __inline__ unsigned long ide_default_io_base(int index) #endif /* MIPS port and memory-mapped I/O string operations. */ +static inline void __ide_flush_prologue(void) +{ +#ifdef CONFIG_SMP + if (cpu_has_dc_aliases) + preempt_disable(); +#endif +} + +static inline void __ide_flush_epilogue(void) +{ +#ifdef CONFIG_SMP + if (cpu_has_dc_aliases) + preempt_enable(); +#endif +} static inline void __ide_flush_dcache_range(unsigned long addr, unsigned long size) { if (cpu_has_dc_aliases) { unsigned long end = addr + size; - for (; addr < end; addr += PAGE_SIZE) - flush_dcache_page(virt_to_page(addr)); + + while (addr < end) { + local_flush_data_cache_page((void *)addr); + addr += PAGE_SIZE; + } } } +/* + * insw() and gang might be called with interrupts disabled, so we can't + * send IPIs for flushing due to the potencial of deadlocks, see the comment + * above smp_call_function() in arch/mips/kernel/smp.c. We work around the + * problem by disabling preemption so we know we actually perform the flush + * on the processor that actually has the lines to be flushed which hopefully + * is even better for performance anyway. + */ static inline void __ide_insw(unsigned long port, void *addr, unsigned int count) { + __ide_flush_prologue(); insw(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 2); + __ide_flush_epilogue(); } static inline void __ide_insl(unsigned long port, void *addr, unsigned int count) { + __ide_flush_prologue(); insl(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 4); + __ide_flush_epilogue(); } static inline void __ide_outsw(unsigned long port, const void *addr, unsigned long count) { + __ide_flush_prologue(); outsw(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 2); + __ide_flush_epilogue(); } static inline void __ide_outsl(unsigned long port, const void *addr, unsigned long count) { + __ide_flush_prologue(); outsl(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 4); + __ide_flush_epilogue(); } static inline void __ide_mm_insw(void __iomem *port, void *addr, u32 count) { + __ide_flush_prologue(); readsw(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 2); + __ide_flush_epilogue(); } static inline void __ide_mm_insl(void __iomem *port, void *addr, u32 count) { + __ide_flush_prologue(); readsl(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 4); + __ide_flush_epilogue(); } static inline void __ide_mm_outsw(void __iomem *port, void *addr, u32 count) { + __ide_flush_prologue(); writesw(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 2); + __ide_flush_epilogue(); } static inline void __ide_mm_outsl(void __iomem * port, void *addr, u32 count) { + __ide_flush_prologue(); writesl(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 4); + __ide_flush_epilogue(); } /* ide_insw calls insw, not __ide_insw. Why? */ -- cgit v1.2.3