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 ++ arch/sh/mm/Kconfig | 3 +++ arch/sh/mm/init.c | 2 ++ 3 files changed, 7 insertions(+) (limited to 'arch') diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 329b3f3051de..4688b890aef8 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(); } } diff --git a/arch/sh/mm/Kconfig b/arch/sh/mm/Kconfig index 12f3d394dc28..b773361892c9 100644 --- a/arch/sh/mm/Kconfig +++ b/arch/sh/mm/Kconfig @@ -218,6 +218,9 @@ endmenu menu "Memory management options" +config QUICKLIST + def_bool y + config MMU bool "Support for memory management hardware" depends on !CPU_SH2 diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 4d030988b368..8fe223a890ed 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -67,6 +67,8 @@ void show_mem(void) printk("%d slab pages\n", slab); printk("%d pages shared\n", shared); printk("%d pages swap cached\n", cached); + printk(KERN_INFO "Total of %ld pages in page table cache\n", + quicklist_total_size()); } #ifdef CONFIG_MMU -- cgit v1.2.2 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') diff --git a/arch/sh/kernel/cpu/sh3/ex.S b/arch/sh/kernel/cpu/sh3/ex.S index ba3082d640b5..2b2a9e02fb75 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 19ca68c71884..8add10bd8268 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 ac8ab57413cc..000000000000 --- 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.2 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') diff --git a/arch/sh/kernel/sh_ksyms.c b/arch/sh/kernel/sh_ksyms.c index fa91641c1f62..c1cfcb9f047c 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.2 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') diff --git a/arch/sh/kernel/cpu/sh2a/Makefile b/arch/sh/kernel/cpu/sh2a/Makefile index 350972ae9410..965fa2572b23 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 000000000000..9704b7926d8b --- /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 a5323364cbca..ffe3e3ee580d 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 4688b890aef8..209cc9b42186 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 eb0191c374b6..d7d98d691c64 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.2 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') diff --git a/arch/sh/kernel/cpu/sh4/fpu.c b/arch/sh/kernel/cpu/sh4/fpu.c index 7624677f6628..d61dd599169f 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 ffe3e3ee580d..a5323364cbca 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 209cc9b42186..9005b2f12aaa 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 d7d98d691c64..b32c35a7c0a3 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 7b40f0ff3dfc..1446d12ba220 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.2 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') diff --git a/arch/sh/kernel/cpu/sh2a/probe.c b/arch/sh/kernel/cpu/sh2a/probe.c index 426f6db01fc6..f455c3509789 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 477d2a854fc4..c27729135935 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.2 From c71861e65e2898850478a7ac6c4b8cc9f7007e9e Mon Sep 17 00:00:00 2001 From: kogiidena Date: Tue, 8 May 2007 20:45:46 +0900 Subject: sh: Fixup ndelay() xloops calculation for alternate HZ. Currently the xloops calculation in ndelay() gets set to 0 when calculated with HZ=250, fix up how we do the HZ factoring in order to get this right for differing values. Signed-off-by: kogiidena Signed-off-by: Paul Mundt --- arch/sh/lib/delay.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/sh/lib/delay.c b/arch/sh/lib/delay.c index 351714694d6d..f3ddd2133e6f 100644 --- a/arch/sh/lib/delay.c +++ b/arch/sh/lib/delay.c @@ -24,9 +24,10 @@ inline void __const_udelay(unsigned long xloops) __asm__("dmulu.l %0, %2\n\t" "sts mach, %0" : "=r" (xloops) - : "0" (xloops), "r" (cpu_data[raw_smp_processor_id()].loops_per_jiffy) + : "0" (xloops), + "r" (HZ * cpu_data[raw_smp_processor_id()].loops_per_jiffy) : "macl", "mach"); - __delay(xloops * HZ); + __delay(xloops); } void __udelay(unsigned long usecs) -- cgit v1.2.2 From f6a3c1ca00ce9a43942364a0cc15601b291df281 Mon Sep 17 00:00:00 2001 From: kogiidena Date: Tue, 8 May 2007 20:49:46 +0900 Subject: sh: landisk mv_nr_irqs definition. Fix up the landisk build. When NR_IRQS was removed, landisk got missed in the updates. Update the machvec for the landisk IRQs to get it working again. Signed-off-by: kogiidena Signed-off-by: Paul Mundt --- arch/sh/boards/landisk/setup.c | 1 + 1 file changed, 1 insertion(+) (limited to 'arch') diff --git a/arch/sh/boards/landisk/setup.c b/arch/sh/boards/landisk/setup.c index a83a5d9587bb..4058b4f50d44 100644 --- a/arch/sh/boards/landisk/setup.c +++ b/arch/sh/boards/landisk/setup.c @@ -93,6 +93,7 @@ static void __init landisk_setup(char **cmdline_p) */ struct sh_machine_vector mv_landisk __initmv = { .mv_name = "LANDISK", + .mv_nr_irqs = 72, .mv_setup = landisk_setup, .mv_init_irq = init_landisk_IRQ, }; -- cgit v1.2.2 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') diff --git a/arch/sh/kernel/syscalls.S b/arch/sh/kernel/syscalls.S index 38fc8cd3ea3a..4357d1a6358f 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.2 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 +--------------- arch/sh/mm/fault.c | 2 +- 3 files changed, 5 insertions(+), 19 deletions(-) (limited to 'arch') diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 9005b2f12aaa..d755589ba8b1 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 1446d12ba220..3a197649cd83 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) diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c index 0ecc117cade4..9207da67ff8a 100644 --- a/arch/sh/mm/fault.c +++ b/arch/sh/mm/fault.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.2 From 1ce7ddd5f4cc754b6afe9eec5cee89ede75348ea Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 9 May 2007 13:20:52 +0900 Subject: sh: Truncate MAX_ACTIVE_REGIONS for the common case. Most SH platforms aren't going to need more than a single active region, ones that need more can pad this out as necessary. Signed-off-by: Paul Mundt --- arch/sh/mm/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'arch') diff --git a/arch/sh/mm/Kconfig b/arch/sh/mm/Kconfig index b773361892c9..253346d7b316 100644 --- a/arch/sh/mm/Kconfig +++ b/arch/sh/mm/Kconfig @@ -303,6 +303,10 @@ config NODES_SHIFT config ARCH_FLATMEM_ENABLE def_bool y +config MAX_ACTIVE_REGIONS + int + default "1" + config ARCH_POPULATES_NODE_MAP def_bool y -- cgit v1.2.2 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/Kconfig | 29 ++---- arch/sh/kernel/process.c | 3 + arch/sh/kernel/time.c | 172 ++++++++++++----------------------- arch/sh/kernel/timers/timer-tmu.c | 182 +++++++++++++++++++++++--------------- 4 files changed, 174 insertions(+), 212 deletions(-) (limited to 'arch') diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index d74eb120a9c6..038179ecf6a9 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -52,6 +52,9 @@ config GENERIC_IOMAP config GENERIC_TIME def_bool n +config GENERIC_CLOCKEVENTS + def_bool n + config SYS_SUPPORTS_APM_EMULATION bool @@ -436,11 +439,11 @@ endmenu menu "Timer and clock configuration" -if !GENERIC_TIME - config SH_TMU bool "TMU timer support" depends on CPU_SH3 || CPU_SH4 + select GENERIC_TIME + select GENERIC_CLOCKEVENTS default y help This enables the use of the TMU as the system timer. @@ -459,8 +462,6 @@ config SH_MTU2 help This enables the use of the MTU2 as the system timer. -endif - config SH_TIMER_IRQ int default "28" if CPU_SUBTYPE_SH7780 || CPU_SUBTYPE_SH7785 @@ -468,24 +469,6 @@ config SH_TIMER_IRQ default "140" if CPU_SUBTYPE_SH7206 default "16" -config NO_IDLE_HZ - bool "Dynamic tick timer" - help - Select this option if you want to disable continuous timer ticks - and have them programmed to occur as required. This option saves - power as the system can remain in idle state for longer. - - By default dynamic tick is disabled during the boot, and can be - manually enabled with: - - echo 1 > /sys/devices/system/timer/timer0/dyn_tick - - Alternatively, if you want dynamic tick automatically enabled - during boot, pass "dyntick=enable" via the kernel command string. - - Please note that dynamic tick may affect the accuracy of - timekeeping on some platforms depending on the implementation. - config SH_PCLK_FREQ int "Peripheral clock frequency (in Hz)" default "27000000" if CPU_SUBTYPE_SH73180 || CPU_SUBTYPE_SH7343 @@ -509,6 +492,8 @@ config SH_CLK_MD help MD2 - MD0 pin setting. +source "kernel/time/Kconfig" + endmenu menu "CPU Frequency scaling" diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index d755589ba8b1..6b4f5748d0be 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 d47e775962e9..a3a67d151e52 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 d9e3151c891e..2d997e2a5b6c 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.2 From fc467a2623029976899261d6d379779c950ddcba Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Wed, 9 May 2007 17:36:15 +0900 Subject: sh: SH7760 DMABRG support. The DMABRG is a special DMA unit within the SH7760 which does data transfers from main memory to Audio units and USB shared memory. It has 3 IRQ lines which generate 10 events, which have to be masked unmasked and acked in a single 32bit register. It works independently from the tradition SH DMAC, but blocks usage of DMAC channel 0. This patch adds 2 functions to associate callbacks with DMABRG events and initialization. Signed-off-by: Manuel Lauss Signed-off-by: Paul Mundt --- arch/sh/drivers/Makefile | 3 +- arch/sh/drivers/dma/Kconfig | 20 +++-- arch/sh/drivers/dma/Makefile | 4 +- arch/sh/drivers/dma/dmabrg.c | 196 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 214 insertions(+), 9 deletions(-) create mode 100644 arch/sh/drivers/dma/dmabrg.c (limited to 'arch') diff --git a/arch/sh/drivers/Makefile b/arch/sh/drivers/Makefile index 6cb92676c5fc..e13f06bebd92 100644 --- a/arch/sh/drivers/Makefile +++ b/arch/sh/drivers/Makefile @@ -2,8 +2,9 @@ # Makefile for the Linux SuperH-specific device drivers. # +obj-y += dma/ + obj-$(CONFIG_PCI) += pci/ -obj-$(CONFIG_SH_DMA) += dma/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_PUSH_SWITCH) += push-switch.o obj-$(CONFIG_HEARTBEAT) += heartbeat.o diff --git a/arch/sh/drivers/dma/Kconfig b/arch/sh/drivers/dma/Kconfig index defc13c37d48..99935f9daf4b 100644 --- a/arch/sh/drivers/dma/Kconfig +++ b/arch/sh/drivers/dma/Kconfig @@ -1,12 +1,12 @@ menu "DMA support" -config SH_DMA - bool "DMA controller (DMAC) support" - help - Selecting this option will provide same API as PC's Direct Memory - Access Controller(8237A) for SuperH DMAC. +config SH_DMA_API + bool - If unsure, say N. +config SH_DMA + bool "SuperH on-chip DMA controller (DMAC) support" + select SH_DMA_API + default n config NR_ONCHIP_DMA_CHANNELS depends on SH_DMA @@ -53,4 +53,12 @@ config DMA_PAGE_OPS_CHANNEL in case channel 3 is unavailable. On the SH4, channels 1,2, and 3 are dual-address capable. +config SH_DMABRG + bool "SH7760 DMABRG support" + depends on CPU_SUBTYPE_SH7760 + help + The DMABRG does data transfers from main memory to Audio/USB units + of the SH7760. + Say Y if you want to use Audio/USB DMA on your SH7760 board. + endmenu diff --git a/arch/sh/drivers/dma/Makefile b/arch/sh/drivers/dma/Makefile index db1295d32268..1ac812d24488 100644 --- a/arch/sh/drivers/dma/Makefile +++ b/arch/sh/drivers/dma/Makefile @@ -2,8 +2,8 @@ # Makefile for the SuperH DMA specific kernel interface routines under Linux. # -obj-y += dma-api.o +obj-$(CONFIG_SH_DMA_API) += dma-api.o dma-sysfs.o obj-$(CONFIG_ISA_DMA_API) += dma-isa.o -obj-$(CONFIG_SYSFS) += dma-sysfs.o obj-$(CONFIG_SH_DMA) += dma-sh.o obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o +obj-$(CONFIG_SH_DMABRG) += dmabrg.o diff --git a/arch/sh/drivers/dma/dmabrg.c b/arch/sh/drivers/dma/dmabrg.c new file mode 100644 index 000000000000..9d0a29370f21 --- /dev/null +++ b/arch/sh/drivers/dma/dmabrg.c @@ -0,0 +1,196 @@ +/* + * SH7760 DMABRG IRQ handling + * + * (c) 2007 MSC Vertriebsges.m.b.H, Manuel Lauss + * licensed under the GPLv2. + * + */ + +#include +#include +#include +#include +#include + +/* + * The DMABRG is a special DMA unit within the SH7760. It does transfers + * from USB-SRAM/Audio units to main memory (and also the LCDC; but that + * part is sensibly placed in the LCDC registers and requires no irqs) + * It has 3 IRQ lines which trigger 10 events, and works independently + * from the traditional SH DMAC (although it blocks usage of DMAC 0) + * + * BRGIRQID | component | dir | meaning | source + * ----------------------------------------------------- + * 0 | USB-DMA | ... | xfer done | DMABRGI1 + * 1 | USB-UAE | ... | USB addr err.| DMABRGI0 + * 2 | HAC0/SSI0 | play| all done | DMABRGI1 + * 3 | HAC0/SSI0 | play| half done | DMABRGI2 + * 4 | HAC0/SSI0 | rec | all done | DMABRGI1 + * 5 | HAC0/SSI0 | rec | half done | DMABRGI2 + * 6 | HAC1/SSI1 | play| all done | DMABRGI1 + * 7 | HAC1/SSI1 | play| half done | DMABRGI2 + * 8 | HAC1/SSI1 | rec | all done | DMABRGI1 + * 9 | HAC1/SSI1 | rec | half done | DMABRGI2 + * + * all can be enabled/disabled in the DMABRGCR register, + * as well as checked if they occured. + * + * DMABRGI0 services USB DMA Address errors, but it still must be + * enabled/acked in the DMABRGCR register. USB-DMA complete indicator + * is grouped together with the audio buffer end indicators, too bad... + * + * DMABRGCR: Bits 31-24: audio-dma ENABLE flags, + * Bits 23-16: audio-dma STATUS flags, + * Bits 9-8: USB error/xfer ENABLE, + * Bits 1-0: USB error/xfer STATUS. + * Ack an IRQ by writing 0 to the STATUS flag. + * Mask IRQ by writing 0 to ENABLE flag. + * + * Usage is almost like with any other IRQ: + * dmabrg_request_irq(BRGIRQID, handler, data) + * dmabrg_free_irq(BRGIRQID) + * + * handler prototype: void brgirqhandler(void *data) + */ + +#define DMARSRA 0xfe090000 +#define DMAOR 0xffa00040 +#define DMACHCR0 0xffa0000c +#define DMABRGCR 0xfe3c0000 + +#define DMAOR_BRG 0x0000c000 +#define DMAOR_DMEN 0x00000001 + +#define DMABRGI0 68 +#define DMABRGI1 69 +#define DMABRGI2 70 + +struct dmabrg_handler { + void (*handler)(void *); + void *data; +} *dmabrg_handlers; + +static inline void dmabrg_call_handler(int i) +{ + dmabrg_handlers[i].handler(dmabrg_handlers[i].data); +} + +/* + * main DMABRG irq handler. It acks irqs and then + * handles every set and unmasked bit sequentially. + * No locking and no validity checks; it should be + * as fast as possible (audio!) + */ +static irqreturn_t dmabrg_irq(int irq, void *data) +{ + unsigned long dcr; + unsigned int i; + + dcr = ctrl_inl(DMABRGCR); + ctrl_outl(dcr & ~0x00ff0003, DMABRGCR); /* ack all */ + dcr &= dcr >> 8; /* ignore masked */ + + /* USB stuff, get it out of the way first */ + if (dcr & 1) + dmabrg_call_handler(DMABRGIRQ_USBDMA); + if (dcr & 2) + dmabrg_call_handler(DMABRGIRQ_USBDMAERR); + + /* Audio */ + dcr >>= 16; + while (dcr) { + i = __ffs(dcr); + dcr &= dcr - 1; + dmabrg_call_handler(i + DMABRGIRQ_A0TXF); + } + return IRQ_HANDLED; +} + +static void dmabrg_disable_irq(unsigned int dmairq) +{ + unsigned long dcr; + dcr = ctrl_inl(DMABRGCR); + dcr &= ~(1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8)); + ctrl_outl(dcr, DMABRGCR); +} + +static void dmabrg_enable_irq(unsigned int dmairq) +{ + unsigned long dcr; + dcr = ctrl_inl(DMABRGCR); + dcr |= (1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8)); + ctrl_outl(dcr, DMABRGCR); +} + +int dmabrg_request_irq(unsigned int dmairq, void(*handler)(void*), + void *data) +{ + if ((dmairq > 9) || !handler) + return -ENOENT; + if (dmabrg_handlers[dmairq].handler) + return -EBUSY; + + dmabrg_handlers[dmairq].handler = handler; + dmabrg_handlers[dmairq].data = data; + + dmabrg_enable_irq(dmairq); + return 0; +} +EXPORT_SYMBOL_GPL(dmabrg_request_irq); + +void dmabrg_free_irq(unsigned int dmairq) +{ + if (likely(dmairq < 10)) { + dmabrg_disable_irq(dmairq); + dmabrg_handlers[dmairq].handler = NULL; + dmabrg_handlers[dmairq].data = NULL; + } +} +EXPORT_SYMBOL_GPL(dmabrg_free_irq); + +static int __init dmabrg_init(void) +{ + unsigned long or; + int ret; + + dmabrg_handlers = kzalloc(10 * sizeof(struct dmabrg_handler), + GFP_KERNEL); + if (!dmabrg_handlers) + return -ENOMEM; + +#ifdef CONFIG_SH_DMA + /* request DMAC channel 0 before anyone else can get it */ + ret = request_dma(0, "DMAC 0 (DMABRG)"); + if (ret < 0) + printk(KERN_INFO "DMABRG: DMAC ch0 not reserved!\n"); +#endif + + ctrl_outl(0, DMABRGCR); + ctrl_outl(0, DMACHCR0); + ctrl_outl(0x94000000, DMARSRA); /* enable DMABRG in DMAC 0 */ + + /* enable DMABRG mode, enable the DMAC */ + or = ctrl_inl(DMAOR); + ctrl_outl(or | DMAOR_BRG | DMAOR_DMEN, DMAOR); + + ret = request_irq(DMABRGI0, dmabrg_irq, IRQF_DISABLED, + "DMABRG USB address error", NULL); + if (ret) + goto out0; + + ret = request_irq(DMABRGI1, dmabrg_irq, IRQF_DISABLED, + "DMABRG Transfer End", NULL); + if (ret) + goto out1; + + ret = request_irq(DMABRGI2, dmabrg_irq, IRQF_DISABLED, + "DMABRG Transfer Half", NULL); + if (ret == 0) + return ret; + + free_irq(DMABRGI1, 0); +out1: free_irq(DMABRGI0, 0); +out0: kfree(dmabrg_handlers); + return ret; +} +subsys_initcall(dmabrg_init); -- cgit v1.2.2 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') diff --git a/arch/sh/kernel/stacktrace.c b/arch/sh/kernel/stacktrace.c index 4bdd2f83535d..d41e561be20e 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.2