From 5f8c9908f200b775a3d6c345bc6f3e928e2426a9 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 11:55:21 +0900 Subject: sh: generic quicklist support. This moves SH over to the generic quicklists. As per x86_64, we have special mappings for the PGDs, so these go on their own list.. Signed-off-by: Paul Mundt --- arch/sh/kernel/process.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 329b3f3051d..4688b890aef 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -18,6 +18,7 @@ #include #include #include +#include #include static int hlt_counter; @@ -64,6 +65,7 @@ void cpu_idle(void) preempt_enable_no_resched(); schedule(); preempt_disable(); + check_pgt_cache(); } } -- cgit v1.2.3 From 00bf0610e4cdbb9fd79ce4718e12b24cd7876dd7 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 13:01:37 +0900 Subject: sh: Share exception vector table for SH-3/4. The only difference between these at the moment are the FPU exceptions, and these are hidden away under CONFIG_SH_FPU (which is only set for the SH-4 case anyways..). This consolidates the two tables, and updates SH-4 to use the updated copy. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh3/ex.S | 13 +++++---- arch/sh/kernel/cpu/sh4/Makefile | 6 ++-- arch/sh/kernel/cpu/sh4/ex.S | 62 ----------------------------------------- 3 files changed, 11 insertions(+), 70 deletions(-) delete mode 100644 arch/sh/kernel/cpu/sh4/ex.S (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/cpu/sh3/ex.S b/arch/sh/kernel/cpu/sh3/ex.S index ba3082d640b..2b2a9e02fb7 100644 --- a/arch/sh/kernel/cpu/sh3/ex.S +++ b/arch/sh/kernel/cpu/sh3/ex.S @@ -1,7 +1,7 @@ /* * arch/sh/kernel/cpu/sh3/ex.S * - * The SH-3 exception vector table. + * The SH-3 and SH-4 exception vector table. * Copyright (C) 1999, 2000, 2002 Niibe Yutaka * Copyright (C) 2003 - 2006 Paul Mundt @@ -9,7 +9,6 @@ * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. - * */ #include @@ -36,8 +35,12 @@ ENTRY(exception_handling_table) .long exception_error ! address error load .long exception_error ! address error store /* 100 */ #endif - .long exception_error ! fpu_exception /* 120 */ - .long exception_error /* 140 */ +#if defined(CONFIG_SH_FPU) + .long do_fpu_error /* 120 */ +#else + .long exception_error /* 120 */ +#endif + .long exception_error /* 140 */ .long system_call ! Unconditional Trap /* 160 */ .long exception_error ! reserved_instruction (filled by trap_init) /* 180 */ .long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/ @@ -55,4 +58,4 @@ ENTRY(user_break_point_trap) * away offsets can be manually inserted in to their appropriate * location via set_exception_table_{evt,vec}(). */ - .balign 4096,0,4096 + .balign 4096,0,4096 diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile index 19ca68c7188..8add10bd826 100644 --- a/arch/sh/kernel/cpu/sh4/Makefile +++ b/arch/sh/kernel/cpu/sh4/Makefile @@ -2,10 +2,10 @@ # Makefile for the Linux/SuperH SH-4 backends. # -obj-y := ex.o probe.o common.o -common-y += $(addprefix ../sh3/, entry.o) +obj-y := probe.o common.o +common-y += $(addprefix ../sh3/, entry.o ex.o) -obj-$(CONFIG_SH_FPU) += fpu.o +obj-$(CONFIG_SH_FPU) += fpu.o obj-$(CONFIG_SH_STORE_QUEUES) += sq.o # CPU subtype setup diff --git a/arch/sh/kernel/cpu/sh4/ex.S b/arch/sh/kernel/cpu/sh4/ex.S deleted file mode 100644 index ac8ab57413c..00000000000 --- a/arch/sh/kernel/cpu/sh4/ex.S +++ /dev/null @@ -1,62 +0,0 @@ -/* - * arch/sh/kernel/cpu/sh4/ex.S - * - * The SH-4 exception vector table. - - * Copyright (C) 1999, 2000, 2002 Niibe Yutaka - * Copyright (C) 2003 - 2006 Paul Mundt - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - */ -#include - - .align 2 - .data - -ENTRY(exception_handling_table) - .long exception_error /* 000 */ - .long exception_error -#if defined(CONFIG_MMU) - .long tlb_miss_load /* 040 */ - .long tlb_miss_store - .long initial_page_write - .long tlb_protection_violation_load - .long tlb_protection_violation_store - .long address_error_load - .long address_error_store /* 100 */ -#else - .long exception_error ! tlb miss load /* 040 */ - .long exception_error ! tlb miss store - .long exception_error ! initial page write - .long exception_error ! tlb prot violation load - .long exception_error ! tlb prot violation store - .long exception_error ! address error load - .long exception_error ! address error store /* 100 */ -#endif -#if defined(CONFIG_SH_FPU) - .long do_fpu_error /* 120 */ -#else - .long exception_error /* 120 */ -#endif - .long exception_error /* 140 */ - .long system_call ! Unconditional Trap /* 160 */ - .long exception_error ! reserved_instruction (filled by trap_init) /* 180 */ - .long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/ -ENTRY(nmi_slot) -#if defined (CONFIG_KGDB_NMI) - .long debug_enter /* 1C0 */ ! Allow trap to debugger -#else - .long exception_none /* 1C0 */ ! Not implemented yet -#endif -ENTRY(user_break_point_trap) - .long break_point_trap /* 1E0 */ - - /* - * Pad the remainder of the table out, exceptions residing in far - * away offsets can be manually inserted in to their appropriate - * location via set_exception_table_{evt,vec}(). - */ - .balign 4096,0,4096 -- cgit v1.2.3 From 51c8b856f5edfa45d956721aa6d6ebaa15699062 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 13:16:57 +0900 Subject: sh: Kill off redundant __div64_32 symbol export. modpost noticed that __div64_32 was being exported twice: WARNING: lib/built-in: '__div64_32' exported twice. Previous export was in arch/sh/kernel/built-in.ko kill off the duplicate. Signed-off-by: Paul Mundt --- arch/sh/kernel/sh_ksyms.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/sh_ksyms.c b/arch/sh/kernel/sh_ksyms.c index fa91641c1f6..c1cfcb9f047 100644 --- a/arch/sh/kernel/sh_ksyms.c +++ b/arch/sh/kernel/sh_ksyms.c @@ -58,8 +58,6 @@ EXPORT_SYMBOL(__udelay); EXPORT_SYMBOL(__ndelay); EXPORT_SYMBOL(__const_udelay); -EXPORT_SYMBOL(__div64_32); - #define DECLARE_EXPORT(name) extern void name(void);EXPORT_SYMBOL(name) /* These symbols are generated by the compiler itself */ -- cgit v1.2.3 From bd0799977cb9b68aa6a39e9630aeea4778a58385 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 14:50:59 +0900 Subject: sh: Support for SH-2A 32-bit opcodes. SH-2A supports both 16 and 32-bit instructions, add a simple helper for figuring out the instruction size in the places where there are hardcoded 16-bit assumptions. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh2a/Makefile | 5 ++- arch/sh/kernel/cpu/sh2a/opcode_helper.c | 55 +++++++++++++++++++++++++++++++++ arch/sh/kernel/kgdb_stub.c | 2 +- arch/sh/kernel/process.c | 5 +-- arch/sh/kernel/signal.c | 8 ++--- 5 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 arch/sh/kernel/cpu/sh2a/opcode_helper.c (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/cpu/sh2a/Makefile b/arch/sh/kernel/cpu/sh2a/Makefile index 350972ae941..965fa2572b2 100644 --- a/arch/sh/kernel/cpu/sh2a/Makefile +++ b/arch/sh/kernel/cpu/sh2a/Makefile @@ -2,9 +2,8 @@ # Makefile for the Linux/SuperH SH-2A backends. # -obj-y := common.o probe.o +obj-y := common.o probe.o opcode_helper.o -common-y += $(addprefix ../sh2/, ex.o) -common-y += $(addprefix ../sh2/, entry.o) +common-y += $(addprefix ../sh2/, ex.o entry.o) obj-$(CONFIG_CPU_SUBTYPE_SH7206) += setup-sh7206.o clock-sh7206.o diff --git a/arch/sh/kernel/cpu/sh2a/opcode_helper.c b/arch/sh/kernel/cpu/sh2a/opcode_helper.c new file mode 100644 index 00000000000..9704b7926d8 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/opcode_helper.c @@ -0,0 +1,55 @@ +/* + * arch/sh/kernel/cpu/sh2a/opcode_helper.c + * + * Helper for the SH-2A 32-bit opcodes. + * + * Copyright (C) 2007 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include + +/* + * Instructions on SH are generally fixed at 16-bits, however, SH-2A + * introduces some 32-bit instructions. Since there are no real + * constraints on their use (and they can be mixed and matched), we need + * to check the instruction encoding to work out if it's a true 32-bit + * instruction or not. + * + * Presently, 32-bit opcodes have only slight variations in what the + * actual encoding looks like in the first-half of the instruction, which + * makes it fairly straightforward to differentiate from the 16-bit ones. + * + * First 16-bits of encoding Used by + * + * 0011nnnnmmmm0001 mov.b, mov.w, mov.l, fmov.d, + * fmov.s, movu.b, movu.w + * + * 0011nnnn0iii1001 bclr.b, bld.b, bset.b, bst.b, band.b, + * bandnot.b, bldnot.b, bor.b, bornot.b, + * bxor.b + * + * 0000nnnniiii0000 movi20 + * 0000nnnniiii0001 movi20s + */ +unsigned int instruction_size(unsigned int insn) +{ + /* Look for the common cases */ + switch ((insn & 0xf00f)) { + case 0x0000: /* movi20 */ + case 0x0001: /* movi20s */ + case 0x3001: /* 32-bit mov/fmov/movu variants */ + return 4; + } + + /* And the special cases.. */ + switch ((insn & 0xf08f)) { + case 0x3009: /* 32-bit b*.b bit operations */ + return 4; + } + + return 2; +} diff --git a/arch/sh/kernel/kgdb_stub.c b/arch/sh/kernel/kgdb_stub.c index a5323364cbc..ffe3e3ee580 100644 --- a/arch/sh/kernel/kgdb_stub.c +++ b/arch/sh/kernel/kgdb_stub.c @@ -867,7 +867,7 @@ static void kgdb_command_loop(const int excep_code, const int trapa_value) trap 0xff, since that indicates a compiled-in breakpoint which will not be replaced (and we would retake the trap forever) */ if ((excep_code == TRAP_VEC) && (trapa_value != (0x3c << 2))) - trap_registers.pc -= 2; + trap_registers.pc -= instruction_size(trap_registers.pc); /* Undo any stepping we may have done */ undo_single_step(); diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 4688b890aef..209cc9b4218 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -19,6 +19,7 @@ #include #include #include +#include #include static int hlt_counter; @@ -497,7 +498,7 @@ asmlinkage void debug_trap_handler(unsigned long r4, unsigned long r5, struct pt_regs *regs = RELOC_HIDE(&__regs, 0); /* Rewind */ - regs->pc -= 2; + regs->pc -= instruction_size(regs->pc); if (notify_die(DIE_TRAP, regs, regs->tra & 0xff, SIGTRAP) == NOTIFY_STOP) @@ -516,7 +517,7 @@ asmlinkage void bug_trap_handler(unsigned long r4, unsigned long r5, struct pt_regs *regs = RELOC_HIDE(&__regs, 0); /* Rewind */ - regs->pc -= 2; + regs->pc -= instruction_size(regs->pc); if (notify_die(DIE_TRAP, regs, TRAPA_BUG_OPCODE & 0xff, SIGTRAP) == NOTIFY_STOP) diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index eb0191c374b..d7d98d691c6 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -23,7 +23,7 @@ #include #include #include - +#include #include #include #include @@ -500,7 +500,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, } /* fallthrough */ case -ERESTARTNOINTR: - regs->pc -= 2; + regs->pc -= instruction_size(regs->pc); } } else { /* gUSA handling */ @@ -600,9 +600,9 @@ static void do_signal(struct pt_regs *regs, unsigned int save_r0) regs->regs[0] == -ERESTARTSYS || regs->regs[0] == -ERESTARTNOINTR) { regs->regs[0] = save_r0; - regs->pc -= 2; + regs->pc -= instruction_size(regs->pc); } else if (regs->regs[0] == -ERESTART_RESTARTBLOCK) { - regs->pc -= 2; + regs->pc -= instruction_size(regs->pc); regs->regs[3] = __NR_restart_syscall; } } -- cgit v1.2.3 From 53f983a90d7908bcece51f86180c7c9b575a1e4d Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 15:31:48 +0900 Subject: sh: Fix PC adjustments for varying opcode length. There are a few different cases for figuring out how to size the instruction. We read in the instruction located at regs->pc - 4 when rewinding the opcode to figure out if there's a 32-bit opcode before the faulting instruction, with a default of a - 2 adjustment on a mismatch. In practice this works for the cases where pc - 4 is just another 16-bit opcode, or we happen to have a 32-bit and a 16-bit immediately preceeding the pc value. In the cases where we aren't rewinding, this is much less ugly.. We also don't bother fixing up the places where we're explicitly dealing with 16-bit instructions, since this might lead to confusion regarding the encoding size possibilities on other CPU variants. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh4/fpu.c | 3 ++- arch/sh/kernel/kgdb_stub.c | 2 +- arch/sh/kernel/process.c | 4 ++-- arch/sh/kernel/signal.c | 11 +++++++---- arch/sh/kernel/traps.c | 4 ++-- 5 files changed, 14 insertions(+), 10 deletions(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/cpu/sh4/fpu.c b/arch/sh/kernel/cpu/sh4/fpu.c index 7624677f662..d61dd599169 100644 --- a/arch/sh/kernel/cpu/sh4/fpu.c +++ b/arch/sh/kernel/cpu/sh4/fpu.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* The PR (precision) bit in the FP Status Register must be clear when @@ -265,7 +266,7 @@ ieee_fpe_handler (struct pt_regs *regs) nextpc = regs->pr; finsn = *(unsigned short *) (regs->pc + 2); } else { - nextpc = regs->pc + 2; + nextpc = regs->pc + instruction_size(insn); finsn = insn; } diff --git a/arch/sh/kernel/kgdb_stub.c b/arch/sh/kernel/kgdb_stub.c index ffe3e3ee580..a5323364cbc 100644 --- a/arch/sh/kernel/kgdb_stub.c +++ b/arch/sh/kernel/kgdb_stub.c @@ -867,7 +867,7 @@ static void kgdb_command_loop(const int excep_code, const int trapa_value) trap 0xff, since that indicates a compiled-in breakpoint which will not be replaced (and we would retake the trap forever) */ if ((excep_code == TRAP_VEC) && (trapa_value != (0x3c << 2))) - trap_registers.pc -= instruction_size(trap_registers.pc); + trap_registers.pc -= 2; /* Undo any stepping we may have done */ undo_single_step(); diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 209cc9b4218..9005b2f12aa 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -498,7 +498,7 @@ asmlinkage void debug_trap_handler(unsigned long r4, unsigned long r5, struct pt_regs *regs = RELOC_HIDE(&__regs, 0); /* Rewind */ - regs->pc -= instruction_size(regs->pc); + regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); if (notify_die(DIE_TRAP, regs, regs->tra & 0xff, SIGTRAP) == NOTIFY_STOP) @@ -517,7 +517,7 @@ asmlinkage void bug_trap_handler(unsigned long r4, unsigned long r5, struct pt_regs *regs = RELOC_HIDE(&__regs, 0); /* Rewind */ - regs->pc -= instruction_size(regs->pc); + regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); if (notify_die(DIE_TRAP, regs, TRAPA_BUG_OPCODE & 0xff, SIGTRAP) == NOTIFY_STOP) diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index d7d98d691c6..b32c35a7c0a 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -500,7 +500,9 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, } /* fallthrough */ case -ERESTARTNOINTR: - regs->pc -= instruction_size(regs->pc); + regs->pc -= instruction_size( + ctrl_inw(regs->pc - 4)); + break; } } else { /* gUSA handling */ @@ -516,7 +518,8 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, regs->regs[15] = regs->regs[1]; if (regs->pc < regs->regs[0]) /* Go to rewind point #1 */ - regs->pc = regs->regs[0] + offset - 2; + regs->pc = regs->regs[0] + offset - + instruction_size(ctrl_inw(regs->pc-4)); } #ifdef CONFIG_PREEMPT local_irq_restore(flags); @@ -600,9 +603,9 @@ static void do_signal(struct pt_regs *regs, unsigned int save_r0) regs->regs[0] == -ERESTARTSYS || regs->regs[0] == -ERESTARTNOINTR) { regs->regs[0] = save_r0; - regs->pc -= instruction_size(regs->pc); + regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); } else if (regs->regs[0] == -ERESTART_RESTARTBLOCK) { - regs->pc -= instruction_size(regs->pc); + regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); regs->regs[3] = __NR_restart_syscall; } } diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 7b40f0ff3df..1446d12ba22 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -505,7 +505,7 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs) simple: ret = handle_unaligned_ins(instruction,regs); if (ret==0) - regs->pc += 2; + regs->pc += instruction_size(instruction); return ret; } #endif /* CONFIG_CPU_SH2A */ @@ -682,7 +682,7 @@ asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5, err = do_fpu_inst(inst, regs); if (!err) { - regs->pc += 2; + regs->pc += instruction_size(inst); return; } /* not a FPU inst. */ -- cgit v1.2.3 From 074f98df0547b7d15f78db9a17e985da0c22af28 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 8 May 2007 15:45:33 +0900 Subject: sh: Add 32-bit opcode feature CPU flag. Add a CPU flag for the CPUs that support 32-bit opcodes, which gets passed down to userspace. Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/sh2a/probe.c | 1 + arch/sh/kernel/setup.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/cpu/sh2a/probe.c b/arch/sh/kernel/cpu/sh2a/probe.c index 426f6db01fc..f455c350978 100644 --- a/arch/sh/kernel/cpu/sh2a/probe.c +++ b/arch/sh/kernel/cpu/sh2a/probe.c @@ -18,6 +18,7 @@ int __init detect_cpu_and_cache_system(void) { /* Just SH7206 for now .. */ current_cpu_data.type = CPU_SH7206; + current_cpu_data.flags |= CPU_HAS_OP32; current_cpu_data.dcache.ways = 4; current_cpu_data.dcache.way_incr = (1 << 11); diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index 477d2a854fc..c2772913593 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -431,7 +431,7 @@ const char *get_cpu_subtype(struct sh_cpuinfo *c) /* Symbolic CPU flags, keep in sync with asm/cpu-features.h */ static const char *cpu_flags[] = { "none", "fpu", "p2flush", "mmuassoc", "dsp", "perfctr", - "ptea", "llsc", "l2", NULL + "ptea", "llsc", "l2", "op32", NULL }; static void show_cpuflags(struct seq_file *m, struct sh_cpuinfo *c) -- cgit v1.2.3 From 21ec4c6453790cb9bfa996ab8f27f22dd47c516a Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 9 May 2007 10:42:48 +0900 Subject: sh: Wire up utimensat syscall. Signed-off-by: Paul Mundt --- arch/sh/kernel/syscalls.S | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/syscalls.S b/arch/sh/kernel/syscalls.S index 38fc8cd3ea3..4357d1a6358 100644 --- a/arch/sh/kernel/syscalls.S +++ b/arch/sh/kernel/syscalls.S @@ -354,3 +354,4 @@ ENTRY(sys_call_table) .long sys_move_pages .long sys_getcpu .long sys_epoll_pwait + .long sys_utimensat /* 320 */ -- cgit v1.2.3 From b118ca572db5af832c6fc1af7b301105378d1a08 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 9 May 2007 10:55:38 +0900 Subject: sh: Convert to common die chain. This went in immediately after SH added the die chain notifiers, so move over to that instead.. Signed-off-by: Paul Mundt --- arch/sh/kernel/process.c | 6 +++--- arch/sh/kernel/traps.c | 16 +--------------- 2 files changed, 4 insertions(+), 18 deletions(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 9005b2f12aa..d755589ba8b 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -500,7 +500,7 @@ asmlinkage void debug_trap_handler(unsigned long r4, unsigned long r5, /* Rewind */ regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); - if (notify_die(DIE_TRAP, regs, regs->tra & 0xff, + if (notify_die(DIE_TRAP, "debug trap", regs, 0, regs->tra & 0xff, SIGTRAP) == NOTIFY_STOP) return; @@ -519,7 +519,7 @@ asmlinkage void bug_trap_handler(unsigned long r4, unsigned long r5, /* Rewind */ regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); - if (notify_die(DIE_TRAP, regs, TRAPA_BUG_OPCODE & 0xff, + if (notify_die(DIE_TRAP, "bug trap", regs, 0, TRAPA_BUG_OPCODE & 0xff, SIGTRAP) == NOTIFY_STOP) return; diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 1446d12ba22..3a197649cd8 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -20,10 +20,10 @@ #include #include #include +#include #include #include #include -#include #ifdef CONFIG_SH_KGDB #include @@ -76,20 +76,6 @@ static void dump_mem(const char *str, unsigned long bottom, unsigned long top) } } -ATOMIC_NOTIFIER_HEAD(shdie_chain); - -int register_die_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&shdie_chain, nb); -} -EXPORT_SYMBOL(register_die_notifier); - -int unregister_die_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&shdie_chain, nb); -} -EXPORT_SYMBOL(unregister_die_notifier); - static DEFINE_SPINLOCK(die_lock); void die(const char * str, struct pt_regs * regs, long err) -- cgit v1.2.3 From 57be2b484a417bffae66359b9b89e7239480b729 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 9 May 2007 17:33:24 +0900 Subject: sh: clockevent/clocksource/hrtimers/nohz TMU support. This adds basic support for clockevents and clocksources, presently only implemented for TMU-based systems (which are the majority of SH-3 and SH-4 systems). The old NO_IDLE_HZ implementation is also dropped completely, the only users of this were on TMU-based systems anyways. More work needs to be done to generalize the TMU handling, in that the current implementation is rather tied to the notion of TMU0 and TMU1 utilization. Additionally, as more SH timers switch over to this scheme, we'll be able to gut most of the remaining system timer infrastructure that existed before. Signed-off-by: Paul Mundt --- arch/sh/kernel/process.c | 3 + arch/sh/kernel/time.c | 172 ++++++++++++----------------------- arch/sh/kernel/timers/timer-tmu.c | 182 +++++++++++++++++++++++--------------- 3 files changed, 167 insertions(+), 190 deletions(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index d755589ba8b..6b4f5748d0b 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -60,8 +61,10 @@ void cpu_idle(void) if (!idle) idle = default_idle; + tick_nohz_stop_sched_tick(); while (!need_resched()) idle(); + tick_nohz_restart_sched_tick(); preempt_enable_no_resched(); schedule(); diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c index d47e775962e..a3a67d151e5 100644 --- a/arch/sh/kernel/time.c +++ b/arch/sh/kernel/time.c @@ -3,7 +3,7 @@ * * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka * Copyright (C) 2000 Philipp Rumpf - * Copyright (C) 2002 - 2006 Paul Mundt + * Copyright (C) 2002 - 2007 Paul Mundt * Copyright (C) 2002 M. R. Brown * * Some code taken from i386 version. @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,14 @@ static int null_rtc_set_time(const time_t secs) return 0; } +/* + * Null high precision timer functions for systems lacking one. + */ +static cycle_t null_hpt_read(void) +{ + return 0; +} + void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time; int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time; @@ -101,6 +110,7 @@ int do_settimeofday(struct timespec *tv) EXPORT_SYMBOL(do_settimeofday); #endif /* !CONFIG_GENERIC_TIME */ +#ifndef CONFIG_GENERIC_CLOCKEVENTS /* last time the RTC clock got updated */ static long last_rtc_update; @@ -138,6 +148,7 @@ void handle_timer_tick(void) last_rtc_update = xtime.tv_sec - 600; } } +#endif /* !CONFIG_GENERIC_CLOCKEVENTS */ #ifdef CONFIG_PM int timer_suspend(struct sys_device *dev, pm_message_t state) @@ -168,136 +179,58 @@ static struct sysdev_class timer_sysclass = { .resume = timer_resume, }; -#ifdef CONFIG_NO_IDLE_HZ -static int timer_dyn_tick_enable(void) +static int __init timer_init_sysfs(void) { - struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; - unsigned long flags; - int ret = -ENODEV; - - if (dyn_tick) { - spin_lock_irqsave(&dyn_tick->lock, flags); - ret = 0; - if (!(dyn_tick->state & DYN_TICK_ENABLED)) { - ret = dyn_tick->enable(); - - if (ret == 0) - dyn_tick->state |= DYN_TICK_ENABLED; - } - spin_unlock_irqrestore(&dyn_tick->lock, flags); - } + int ret = sysdev_class_register(&timer_sysclass); + if (ret != 0) + return ret; - return ret; + sys_timer->dev.cls = &timer_sysclass; + return sysdev_register(&sys_timer->dev); } +device_initcall(timer_init_sysfs); -static int timer_dyn_tick_disable(void) -{ - struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; - unsigned long flags; - int ret = -ENODEV; - - if (dyn_tick) { - spin_lock_irqsave(&dyn_tick->lock, flags); - ret = 0; - if (dyn_tick->state & DYN_TICK_ENABLED) { - ret = dyn_tick->disable(); - - if (ret == 0) - dyn_tick->state &= ~DYN_TICK_ENABLED; - } - spin_unlock_irqrestore(&dyn_tick->lock, flags); - } - - return ret; -} +void (*board_time_init)(void); /* - * Reprogram the system timer for at least the calculated time interval. - * This function should be called from the idle thread with IRQs disabled, - * immediately before sleeping. + * Shamelessly based on the MIPS and Sparc64 work. */ -void timer_dyn_reprogram(void) -{ - struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; - unsigned long next, seq, flags; - - if (!dyn_tick) - return; - - spin_lock_irqsave(&dyn_tick->lock, flags); - if (dyn_tick->state & DYN_TICK_ENABLED) { - next = next_timer_interrupt(); - do { - seq = read_seqbegin(&xtime_lock); - dyn_tick->reprogram(next - jiffies); - } while (read_seqretry(&xtime_lock, seq)); - } - spin_unlock_irqrestore(&dyn_tick->lock, flags); -} +static unsigned long timer_ticks_per_nsec_quotient __read_mostly; +unsigned long sh_hpt_frequency = 0; + +#define NSEC_PER_CYC_SHIFT 10 + +struct clocksource clocksource_sh = { + .name = "SuperH", + .rating = 200, + .mask = CLOCKSOURCE_MASK(32), + .read = null_hpt_read, + .shift = 16, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; -static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf) +static void __init init_sh_clocksource(void) { - return sprintf(buf, "%i\n", - (sys_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1); -} + if (!sh_hpt_frequency || clocksource_sh.read == null_hpt_read) + return; -static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf, - size_t count) -{ - unsigned int enable = simple_strtoul(buf, NULL, 2); + clocksource_sh.mult = clocksource_hz2mult(sh_hpt_frequency, + clocksource_sh.shift); - if (enable) - timer_dyn_tick_enable(); - else - timer_dyn_tick_disable(); + timer_ticks_per_nsec_quotient = + clocksource_hz2mult(sh_hpt_frequency, NSEC_PER_CYC_SHIFT); - return count; + clocksource_register(&clocksource_sh); } -static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick); -/* - * dyntick=enable|disable - */ -static char dyntick_str[4] __initdata = ""; - -static int __init dyntick_setup(char *str) +#ifdef CONFIG_GENERIC_TIME +unsigned long long sched_clock(void) { - if (str) - strlcpy(dyntick_str, str, sizeof(dyntick_str)); - return 1; + unsigned long long ticks = clocksource_sh.read(); + return (ticks * timer_ticks_per_nsec_quotient) >> NSEC_PER_CYC_SHIFT; } - -__setup("dyntick=", dyntick_setup); -#endif - -static int __init timer_init_sysfs(void) -{ - int ret = sysdev_class_register(&timer_sysclass); - if (ret != 0) - return ret; - - sys_timer->dev.cls = &timer_sysclass; - ret = sysdev_register(&sys_timer->dev); - -#ifdef CONFIG_NO_IDLE_HZ - if (ret == 0 && sys_timer->dyn_tick) { - ret = sysdev_create_file(&sys_timer->dev, &attr_dyn_tick); - - /* - * Turn on dynamic tick after calibrate delay - * for correct bogomips - */ - if (ret == 0 && dyntick_str[0] == 'e') - ret = timer_dyn_tick_enable(); - } #endif - return ret; -} -device_initcall(timer_init_sysfs); - -void (*board_time_init)(void); - void __init time_init(void) { if (board_time_init) @@ -316,10 +249,15 @@ void __init time_init(void) sys_timer = get_sys_timer(); printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); -#ifdef CONFIG_NO_IDLE_HZ - if (sys_timer->dyn_tick) - spin_lock_init(&sys_timer->dyn_tick->lock); -#endif + if (sys_timer->ops->read) + clocksource_sh.read = sys_timer->ops->read; + + init_sh_clocksource(); + + if (sh_hpt_frequency) + printk("Using %lu.%03lu MHz high precision timer.\n", + ((sh_hpt_frequency + 500) / 1000) / 1000, + ((sh_hpt_frequency + 500) / 1000) % 1000); #if defined(CONFIG_SH_KGDB) /* diff --git a/arch/sh/kernel/timers/timer-tmu.c b/arch/sh/kernel/timers/timer-tmu.c index d9e3151c891..2d997e2a5b6 100644 --- a/arch/sh/kernel/timers/timer-tmu.c +++ b/arch/sh/kernel/timers/timer-tmu.c @@ -1,7 +1,7 @@ /* * arch/sh/kernel/timers/timer-tmu.c - TMU Timer Support * - * Copyright (C) 2005 Paul Mundt + * Copyright (C) 2005 - 2007 Paul Mundt * * TMU handling code hacked out of arch/sh/kernel/time.c * @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -25,56 +26,75 @@ #include #define TMU_TOCR_INIT 0x00 -#define TMU0_TCR_INIT 0x0020 -#define TMU_TSTR_INIT 1 +#define TMU_TCR_INIT 0x0020 -#define TMU0_TCR_CALIB 0x0000 +static int tmu_timer_start(void) +{ + ctrl_outb(ctrl_inb(TMU_TSTR) | 0x3, TMU_TSTR); + return 0; +} -static unsigned long tmu_timer_get_offset(void) +static void tmu0_timer_set_interval(unsigned long interval, unsigned int reload) { - int count; - static int count_p = 0x7fffffff; /* for the first call after boot */ - static unsigned long jiffies_p = 0; + ctrl_outl(interval, TMU0_TCNT); /* - * cache volatile jiffies temporarily; we have IRQs turned off. + * TCNT reloads from TCOR on underflow, clear it if we don't + * intend to auto-reload */ - unsigned long jiffies_t; + if (reload) + ctrl_outl(interval, TMU0_TCOR); + else + ctrl_outl(0, TMU0_TCOR); - /* timer count may underflow right here */ - count = ctrl_inl(TMU0_TCNT); /* read the latched count */ + tmu_timer_start(); +} - jiffies_t = jiffies; +static int tmu_timer_stop(void) +{ + ctrl_outb(ctrl_inb(TMU_TSTR) & ~0x3, TMU_TSTR); + return 0; +} - /* - * avoiding timer inconsistencies (they are rare, but they happen)... - * there is one kind of problem that must be avoided here: - * 1. the timer counter underflows - */ +static cycle_t tmu_timer_read(void) +{ + return ~ctrl_inl(TMU1_TCNT); +} + +static int tmu_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + tmu0_timer_set_interval(cycles, 1); + return 0; +} - if (jiffies_t == jiffies_p) { - if (count > count_p) { - /* the nutcase */ - if (ctrl_inw(TMU0_TCR) & 0x100) { /* Check UNF bit */ - count -= LATCH; - } else { - printk("%s (): hardware timer problem?\n", - __FUNCTION__); - } - } - } else - jiffies_p = jiffies_t; - - count_p = count; - - count = ((LATCH-1) - count) * TICK_SIZE; - count = (count + LATCH/2) / LATCH; - - return count; +static void tmu_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + ctrl_outl(ctrl_inl(TMU0_TCNT), TMU0_TCOR); + break; + case CLOCK_EVT_MODE_ONESHOT: + ctrl_outl(0, TMU0_TCOR); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + break; + } } +static struct clock_event_device tmu0_clockevent = { + .name = "tmu0", + .shift = 32, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = tmu_set_mode, + .set_next_event = tmu_set_next_event, +}; + static irqreturn_t tmu_timer_interrupt(int irq, void *dummy) { + struct clock_event_device *evt = &tmu0_clockevent; unsigned long timer_status; /* Clear UNF bit */ @@ -82,72 +102,76 @@ static irqreturn_t tmu_timer_interrupt(int irq, void *dummy) timer_status &= ~0x100; ctrl_outw(timer_status, TMU0_TCR); - /* - * Here we are in the timer irq handler. We just have irqs locally - * disabled but we don't know if the timer_bh is running on the other - * CPU. We need to avoid to SMP race with it. NOTE: we don' t need - * the irq version of write_lock because as just said we have irq - * locally disabled. -arca - */ - write_seqlock(&xtime_lock); - handle_timer_tick(); - write_sequnlock(&xtime_lock); + evt->event_handler(evt); return IRQ_HANDLED; } -static struct irqaction tmu_irq = { - .name = "timer", +static struct irqaction tmu0_irq = { + .name = "periodic timer", .handler = tmu_timer_interrupt, .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, .mask = CPU_MASK_NONE, }; -static void tmu_clk_init(struct clk *clk) +static void tmu0_clk_init(struct clk *clk) { - u8 divisor = TMU0_TCR_INIT & 0x7; - ctrl_outw(TMU0_TCR_INIT, TMU0_TCR); + u8 divisor = TMU_TCR_INIT & 0x7; + ctrl_outw(TMU_TCR_INIT, TMU0_TCR); clk->rate = clk->parent->rate / (4 << (divisor << 1)); } -static void tmu_clk_recalc(struct clk *clk) +static void tmu0_clk_recalc(struct clk *clk) { u8 divisor = ctrl_inw(TMU0_TCR) & 0x7; clk->rate = clk->parent->rate / (4 << (divisor << 1)); } -static struct clk_ops tmu_clk_ops = { - .init = tmu_clk_init, - .recalc = tmu_clk_recalc, +static struct clk_ops tmu0_clk_ops = { + .init = tmu0_clk_init, + .recalc = tmu0_clk_recalc, }; static struct clk tmu0_clk = { .name = "tmu0_clk", - .ops = &tmu_clk_ops, + .ops = &tmu0_clk_ops, }; -static int tmu_timer_start(void) +static void tmu1_clk_init(struct clk *clk) { - ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); - return 0; + u8 divisor = TMU_TCR_INIT & 0x7; + ctrl_outw(divisor, TMU1_TCR); + clk->rate = clk->parent->rate / (4 << (divisor << 1)); } -static int tmu_timer_stop(void) +static void tmu1_clk_recalc(struct clk *clk) { - ctrl_outb(0, TMU_TSTR); - return 0; + u8 divisor = ctrl_inw(TMU1_TCR) & 0x7; + clk->rate = clk->parent->rate / (4 << (divisor << 1)); } +static struct clk_ops tmu1_clk_ops = { + .init = tmu1_clk_init, + .recalc = tmu1_clk_recalc, +}; + +static struct clk tmu1_clk = { + .name = "tmu1_clk", + .ops = &tmu1_clk_ops, +}; + static int tmu_timer_init(void) { unsigned long interval; + unsigned long frequency; - setup_irq(CONFIG_SH_TIMER_IRQ, &tmu_irq); + setup_irq(CONFIG_SH_TIMER_IRQ, &tmu0_irq); tmu0_clk.parent = clk_get(NULL, "module_clk"); + tmu1_clk.parent = clk_get(NULL, "module_clk"); - /* Start TMU0 */ tmu_timer_stop(); + #if !defined(CONFIG_CPU_SUBTYPE_SH7300) && \ !defined(CONFIG_CPU_SUBTYPE_SH7760) && \ !defined(CONFIG_CPU_SUBTYPE_SH7785) @@ -155,15 +179,29 @@ static int tmu_timer_init(void) #endif clk_register(&tmu0_clk); + clk_register(&tmu1_clk); clk_enable(&tmu0_clk); + clk_enable(&tmu1_clk); - interval = (clk_get_rate(&tmu0_clk) + HZ / 2) / HZ; - printk(KERN_INFO "Interval = %ld\n", interval); + frequency = clk_get_rate(&tmu0_clk); + interval = (frequency + HZ / 2) / HZ; - ctrl_outl(interval, TMU0_TCOR); - ctrl_outl(interval, TMU0_TCNT); + sh_hpt_frequency = clk_get_rate(&tmu1_clk); + ctrl_outl(~0, TMU1_TCNT); + ctrl_outl(~0, TMU1_TCOR); - tmu_timer_start(); + tmu0_timer_set_interval(interval, 1); + + tmu0_clockevent.mult = div_sc(frequency, NSEC_PER_SEC, + tmu0_clockevent.shift); + tmu0_clockevent.max_delta_ns = + clockevent_delta2ns(-1, &tmu0_clockevent); + tmu0_clockevent.min_delta_ns = + clockevent_delta2ns(1, &tmu0_clockevent); + + tmu0_clockevent.cpumask = cpumask_of_cpu(0); + + clockevents_register_device(&tmu0_clockevent); return 0; } @@ -172,9 +210,7 @@ struct sys_timer_ops tmu_timer_ops = { .init = tmu_timer_init, .start = tmu_timer_start, .stop = tmu_timer_stop, -#ifndef CONFIG_GENERIC_TIME - .get_offset = tmu_timer_get_offset, -#endif + .read = tmu_timer_read, }; struct sys_timer tmu_timer = { -- cgit v1.2.3 From a3cf4ea8729a5d448742fd5a0a003827c9f25cb6 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 9 May 2007 18:55:14 +0900 Subject: sh: Fix stacktrace simplification fallout. When the stacktrace simplification changes went in the function definition on SH got skipped, fix it up so things build again. Signed-off-by: Paul Mundt --- arch/sh/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/stacktrace.c b/arch/sh/kernel/stacktrace.c index 4bdd2f83535..d41e561be20 100644 --- a/arch/sh/kernel/stacktrace.c +++ b/arch/sh/kernel/stacktrace.c @@ -17,7 +17,7 @@ /* * Save stack-backtrace addresses into a stack_trace buffer. */ -void save_stack_trace(struct stack_trace *trace, struct task_struct *task) +void save_stack_trace(struct stack_trace *trace) { unsigned long *sp = (unsigned long *)current_stack_pointer; -- cgit v1.2.3