From 742fd1bcfb475c702c9b1dd6afc79c08f8dbf7dd Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 7 Aug 2008 17:36:12 +0900 Subject: sh: Provide movli.l/movco.l-based bitops. Signed-off-by: Paul Mundt --- arch/sh/include/asm/bitops-llsc.h | 144 ++++++++++++++++++++++++++++++++++++++ arch/sh/include/asm/bitops.h | 2 + 2 files changed, 146 insertions(+) create mode 100644 arch/sh/include/asm/bitops-llsc.h (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/bitops-llsc.h b/arch/sh/include/asm/bitops-llsc.h new file mode 100644 index 000000000000..43b8e1a8239e --- /dev/null +++ b/arch/sh/include/asm/bitops-llsc.h @@ -0,0 +1,144 @@ +#ifndef __ASM_SH_BITOPS_LLSC_H +#define __ASM_SH_BITOPS_LLSC_H + +static inline void set_bit(int nr, volatile void * addr) +{ + int mask; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + + __asm__ __volatile__ ( + "1: \n\t" + "movli.l @%1, %0 ! set_bit \n\t" + "or %3, %0 \n\t" + "movco.l %0, @%1 \n\t" + "bf 1b \n\t" + : "=&z" (tmp), "=r" (a) + : "1" (a), "r" (mask) + : "t", "memory" + ); +} + +static inline void clear_bit(int nr, volatile void * addr) +{ + int mask; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + + __asm__ __volatile__ ( + "1: \n\t" + "movli.l @%1, %0 ! clear_bit \n\t" + "and %3, %0 \n\t" + "movco.l %0, @%1 \n\t" + "bf 1b \n\t" + : "=&z" (tmp), "=r" (a) + : "1" (a), "r" (~mask) + : "t", "memory" + ); +} + +static inline void change_bit(int nr, volatile void * addr) +{ + int mask; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + + __asm__ __volatile__ ( + "1: \n\t" + "movli.l @%1, %0 ! change_bit \n\t" + "xor %3, %0 \n\t" + "movco.l %0, @%1 \n\t" + "bf 1b \n\t" + : "=&z" (tmp), "=r" (a) + : "1" (a), "r" (mask) + : "t", "memory" + ); +} + +static inline int test_and_set_bit(int nr, volatile void * addr) +{ + int mask, retval; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + + __asm__ __volatile__ ( + "1: \n\t" + "movli.l @%1, %0 ! test_and_set_bit \n\t" + "mov %0, %2 \n\t" + "or %4, %0 \n\t" + "movco.l %0, @%1 \n\t" + "bf 1b \n\t" + "and %4, %2 \n\t" + : "=&z" (tmp), "=r" (a), "=&r" (retval) + : "1" (a), "r" (mask) + : "t", "memory" + ); + + return retval != 0; +} + +static inline int test_and_clear_bit(int nr, volatile void * addr) +{ + int mask, retval; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + + __asm__ __volatile__ ( + "1: \n\t" + "movli.l @%1, %0 ! test_and_clear_bit \n\t" + "mov %0, %2 \n\t" + "and %5, %0 \n\t" + "movco.l %0, @%1 \n\t" + "bf 1b \n\t" + "and %4, %2 \n\t" + "synco \n\t" + : "=&z" (tmp), "=r" (a), "=&r" (retval) + : "1" (a), "r" (mask), "r" (~mask) + : "t", "memory" + ); + + return retval != 0; +} + +static inline int test_and_change_bit(int nr, volatile void * addr) +{ + int mask, retval; + volatile unsigned int *a = addr; + unsigned long tmp; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + + __asm__ __volatile__ ( + "1: \n\t" + "movli.l @%1, %0 ! test_and_change_bit \n\t" + "mov %0, %2 \n\t" + "xor %4, %0 \n\t" + "movco.l %0, @%1 \n\t" + "bf 1b \n\t" + "and %4, %2 \n\t" + "synco \n\t" + : "=&z" (tmp), "=r" (a), "=&r" (retval) + : "1" (a), "r" (mask) + : "t", "memory" + ); + + return retval != 0; +} + +#endif /* __ASM_SH_BITOPS_LLSC_H */ diff --git a/arch/sh/include/asm/bitops.h b/arch/sh/include/asm/bitops.h index d7d382f63ee5..367930d8e5ae 100644 --- a/arch/sh/include/asm/bitops.h +++ b/arch/sh/include/asm/bitops.h @@ -13,6 +13,8 @@ #ifdef CONFIG_GUSA_RB #include +#elif defined(CONFIG_CPU_SH4A) +#include #else #include #endif -- cgit v1.2.3 From ee43a8442bd7a5d611f11958e6f8c8953d26f907 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 7 Aug 2008 18:01:43 +0900 Subject: sh: Provide movli.l/movco.l-based cmpxchg. Signed-off-by: Paul Mundt --- arch/sh/include/asm/cmpxchg-llsc.h | 71 ++++++++++++++++++++++++++++++++++++++ arch/sh/include/asm/system.h | 2 ++ 2 files changed, 73 insertions(+) create mode 100644 arch/sh/include/asm/cmpxchg-llsc.h (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/cmpxchg-llsc.h b/arch/sh/include/asm/cmpxchg-llsc.h new file mode 100644 index 000000000000..aee3bf286581 --- /dev/null +++ b/arch/sh/include/asm/cmpxchg-llsc.h @@ -0,0 +1,71 @@ +#ifndef __ASM_SH_CMPXCHG_LLSC_H +#define __ASM_SH_CMPXCHG_LLSC_H + +static inline unsigned long xchg_u32(volatile u32 *m, unsigned long val) +{ + unsigned long retval; + unsigned long tmp; + + __asm__ __volatile__ ( + "1: \n\t" + "movli.l @%1, %0 ! xchg_u32 \n\t" + "mov %0, %2 \n\t" + "mov %4, %0 \n\t" + "movco.l %0, @%1 \n\t" + "bf 1b \n\t" + "synco \n\t" + : "=&z"(tmp), "=r" (m), "=&r" (retval) + : "1" (m), "r" (val) + : "t", "memory" + ); + + return retval; +} + +static inline unsigned long xchg_u8(volatile u8 *m, unsigned long val) +{ + unsigned long retval; + unsigned long tmp; + + __asm__ __volatile__ ( + "1: \n\t" + "movli.l @%1, %0 ! xchg_u8 \n\t" + "mov %0, %2 \n\t" + "mov %4, %0 \n\t" + "movco.l %0, @%1 \n\t" + "bf 1b \n\t" + "synco \n\t" + : "=&z"(tmp), "=r" (m), "=&r" (retval) + : "1" (m), "r" (val & 0xff) + : "t", "memory" + ); + + return retval; +} + +static inline unsigned long +__cmpxchg_u32(volatile int *m, unsigned long old, unsigned long new) +{ + unsigned long retval; + unsigned long tmp; + + __asm__ __volatile__ ( + "1: \n\t" + "movli.l @%1, %0 ! __cmpxchg_u32 \n\t" + "mov %0, %2 \n\t" + "cmp/eq %2, %4 \n\t" + "bf 2f \n\t" + "mov %5, %0 \n\t" + "2: \n\t" + "movco.l %0, @%1 \n\t" + "bf 1b \n\t" + "synco \n\t" + : "=&z" (tmp), "=r" (m), "=&r" (retval) + : "1" (m), "r" (old), "r" (new) + : "t", "memory" + ); + + return retval; +} + +#endif /* __ASM_SH_CMPXCHG_LLSC_H */ diff --git a/arch/sh/include/asm/system.h b/arch/sh/include/asm/system.h index 056d68cd2108..fbac113bbfbf 100644 --- a/arch/sh/include/asm/system.h +++ b/arch/sh/include/asm/system.h @@ -70,6 +70,8 @@ #ifdef CONFIG_GUSA_RB #include +#elif defined(CONFIG_CPU_SH4A) +#include #else #include #endif -- cgit v1.2.3 From 04ec080dcaad7d3c6d0b40b599c8e63da618f784 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 6 Aug 2008 17:29:14 +0900 Subject: sh: Kill off unused defines from asm/smp.h. Signed-off-by: Paul Mundt --- arch/sh/include/asm/smp.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/smp.h b/arch/sh/include/asm/smp.h index 593343cd26ee..e2b79e6864e7 100644 --- a/arch/sh/include/asm/smp.h +++ b/arch/sh/include/asm/smp.h @@ -21,11 +21,6 @@ extern int __cpu_number_map[NR_CPUS]; extern int __cpu_logical_map[NR_CPUS]; #define cpu_logical_map(cpu) __cpu_logical_map[cpu] -/* I've no idea what the real meaning of this is */ -#define PROC_CHANGE_PENALTY 20 - -#define NO_PROC_ID (-1) - #define SMP_MSG_FUNCTION 0 #define SMP_MSG_RESCHEDULE 1 #define SMP_MSG_FUNCTION_SINGLE 2 -- cgit v1.2.3 From 173a44dd1f406e9aa6fcf46c83b7c972d10ec930 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 6 Aug 2008 18:02:48 +0900 Subject: sh: smp: Provide a generic IPI handler. This provides a generic smp_message_recv() routine (based on the PPC one), that IPI IRQs can wrap in to. Signed-off-by: Paul Mundt --- arch/sh/include/asm/smp.h | 1 + arch/sh/kernel/smp.c | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/smp.h b/arch/sh/include/asm/smp.h index e2b79e6864e7..1292c6d3c53e 100644 --- a/arch/sh/include/asm/smp.h +++ b/arch/sh/include/asm/smp.h @@ -26,6 +26,7 @@ extern int __cpu_logical_map[NR_CPUS]; #define SMP_MSG_FUNCTION_SINGLE 2 #define SMP_MSG_NR 3 +void smp_message_recv(unsigned int msg); void plat_smp_setup(void); void plat_prepare_cpus(unsigned int max_cpus); int plat_smp_processor_id(void); diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c index 60c50841143e..ebfdd364544f 100644 --- a/arch/sh/kernel/smp.c +++ b/arch/sh/kernel/smp.c @@ -3,7 +3,7 @@ * * SMP support for the SuperH processors. * - * Copyright (C) 2002 - 2007 Paul Mundt + * Copyright (C) 2002 - 2008 Paul Mundt * Copyright (C) 2006 - 2007 Akio Idehara * * This file is subject to the terms and conditions of the GNU General Public @@ -184,6 +184,24 @@ void arch_send_call_function_single_ipi(int cpu) plat_send_ipi(cpu, SMP_MSG_FUNCTION_SINGLE); } +void smp_message_recv(unsigned int msg) +{ + switch (msg) { + case SMP_MSG_FUNCTION: + generic_smp_call_function_interrupt(); + break; + case SMP_MSG_RESCHEDULE: + break; + case SMP_MSG_FUNCTION_SINGLE: + generic_smp_call_function_single_interrupt(); + break; + default: + printk(KERN_WARNING "SMP %d: %s(): unknown IPI %d\n", + smp_processor_id(), __func__, msg); + break; + } +} + /* Not really SMP stuff ... */ int setup_profiling_timer(unsigned int multiplier) { -- cgit v1.2.3 From c7936b9abcf5e043e73f183a37e81787f6178dd0 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 6 Aug 2008 18:05:09 +0900 Subject: sh: smp: Hook in to the generic IPI handler for SH-X3 SMP. Signed-off-by: Paul Mundt --- arch/sh/include/asm/smp.h | 2 -- arch/sh/kernel/cpu/sh4a/smp-shx3.c | 65 ++++++++++++++------------------------ 2 files changed, 24 insertions(+), 43 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/smp.h b/arch/sh/include/asm/smp.h index 1292c6d3c53e..5ebe0d09559e 100644 --- a/arch/sh/include/asm/smp.h +++ b/arch/sh/include/asm/smp.h @@ -32,8 +32,6 @@ void plat_prepare_cpus(unsigned int max_cpus); int plat_smp_processor_id(void); void plat_start_cpu(unsigned int cpu, unsigned long entry_point); void plat_send_ipi(unsigned int cpu, unsigned int message); -int plat_register_ipi_handler(unsigned int message, - void (*handler)(void *), void *arg); extern void arch_send_call_function_single_ipi(int cpu); extern void arch_send_call_function_ipi(cpumask_t mask); diff --git a/arch/sh/kernel/cpu/sh4a/smp-shx3.c b/arch/sh/kernel/cpu/sh4a/smp-shx3.c index e5e06845fa43..edb4da037e0c 100644 --- a/arch/sh/kernel/cpu/sh4a/smp-shx3.c +++ b/arch/sh/kernel/cpu/sh4a/smp-shx3.c @@ -1,7 +1,7 @@ /* * SH-X3 SMP * - * Copyright (C) 2007 Paul Mundt + * Copyright (C) 2007 - 2008 Paul Mundt * Copyright (C) 2007 Magnus Damm * * This file is subject to the terms and conditions of the GNU General Public @@ -14,6 +14,22 @@ #include #include +static irqreturn_t ipi_interrupt_handler(int irq, void *arg) +{ + unsigned int message = (unsigned int)(long)arg; + unsigned int cpu = hard_smp_processor_id(); + unsigned int offs = 4 * cpu; + unsigned int x; + + x = ctrl_inl(0xfe410070 + offs); /* C0INITICI..CnINTICI */ + x &= (1 << (message << 2)); + ctrl_outl(x, 0xfe410080 + offs); /* C0INTICICLR..CnINTICICLR */ + + smp_message_recv(message); + + return IRQ_HANDLED; +} + void __init plat_smp_setup(void) { unsigned int cpu = 0; @@ -40,6 +56,13 @@ void __init plat_smp_setup(void) void __init plat_prepare_cpus(unsigned int max_cpus) { + int i; + + BUILD_BUG_ON(SMP_MSG_NR >= 8); + + for (i = 0; i < SMP_MSG_NR; i++) + request_irq(104 + i, ipi_interrupt_handler, IRQF_DISABLED, + "IPI", (void *)(long)i); } #define STBCR_REG(phys_id) (0xfe400004 | (phys_id << 12)) @@ -75,46 +98,6 @@ void plat_send_ipi(unsigned int cpu, unsigned int message) unsigned long addr = 0xfe410070 + (cpu * 4); BUG_ON(cpu >= 4); - BUG_ON(message >= SMP_MSG_NR); ctrl_outl(1 << (message << 2), addr); /* C0INTICI..CnINTICI */ } - -struct ipi_data { - void (*handler)(void *); - void *arg; - unsigned int message; -}; - -static irqreturn_t ipi_interrupt_handler(int irq, void *arg) -{ - struct ipi_data *id = arg; - unsigned int cpu = hard_smp_processor_id(); - unsigned int offs = 4 * cpu; - unsigned int x; - - x = ctrl_inl(0xfe410070 + offs); /* C0INITICI..CnINTICI */ - x &= (1 << (id->message << 2)); - ctrl_outl(x, 0xfe410080 + offs); /* C0INTICICLR..CnINTICICLR */ - - id->handler(id->arg); - - return IRQ_HANDLED; -} - -static struct ipi_data ipi_handlers[SMP_MSG_NR]; - -int plat_register_ipi_handler(unsigned int message, - void (*handler)(void *), void *arg) -{ - struct ipi_data *id = &ipi_handlers[message]; - - BUG_ON(SMP_MSG_NR >= 8); - BUG_ON(message >= SMP_MSG_NR); - - id->handler = handler; - id->arg = arg; - id->message = message; - - return request_irq(104 + message, ipi_interrupt_handler, 0, "IPI", id); -} -- cgit v1.2.3 From 6f52707e6882eb3bc6920c3f59beb05d23d68354 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 6 Aug 2008 18:21:03 +0900 Subject: sh: smp: Hook up a timer IPI stub. Signed-off-by: Paul Mundt --- arch/sh/include/asm/smp.h | 19 +++++++++++++------ arch/sh/kernel/smp.c | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/smp.h b/arch/sh/include/asm/smp.h index 5ebe0d09559e..9d22cda67c29 100644 --- a/arch/sh/include/asm/smp.h +++ b/arch/sh/include/asm/smp.h @@ -21,19 +21,26 @@ extern int __cpu_number_map[NR_CPUS]; extern int __cpu_logical_map[NR_CPUS]; #define cpu_logical_map(cpu) __cpu_logical_map[cpu] -#define SMP_MSG_FUNCTION 0 -#define SMP_MSG_RESCHEDULE 1 -#define SMP_MSG_FUNCTION_SINGLE 2 -#define SMP_MSG_NR 3 +enum { + SMP_MSG_FUNCTION, + SMP_MSG_RESCHEDULE, + SMP_MSG_FUNCTION_SINGLE, + SMP_MSG_TIMER, + + SMP_MSG_NR, /* must be last */ +}; void smp_message_recv(unsigned int msg); +void smp_timer_broadcast(cpumask_t mask); + void plat_smp_setup(void); void plat_prepare_cpus(unsigned int max_cpus); int plat_smp_processor_id(void); void plat_start_cpu(unsigned int cpu, unsigned long entry_point); void plat_send_ipi(unsigned int cpu, unsigned int message); -extern void arch_send_call_function_single_ipi(int cpu); -extern void arch_send_call_function_ipi(cpumask_t mask); + +void arch_send_call_function_single_ipi(int cpu); +void arch_send_call_function_ipi(cpumask_t mask); #else diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c index ebfdd364544f..9cb3734dbd49 100644 --- a/arch/sh/kernel/smp.c +++ b/arch/sh/kernel/smp.c @@ -184,6 +184,21 @@ void arch_send_call_function_single_ipi(int cpu) plat_send_ipi(cpu, SMP_MSG_FUNCTION_SINGLE); } +void smp_timer_broadcast(cpumask_t mask) +{ + int cpu; + + for_each_cpu_mask(cpu, mask) + plat_send_ipi(cpu, SMP_MSG_TIMER); +} + +static void ipi_timer(void) +{ + irq_enter(); + /* XXX ... */ + irq_exit(); +} + void smp_message_recv(unsigned int msg) { switch (msg) { @@ -195,6 +210,9 @@ void smp_message_recv(unsigned int msg) case SMP_MSG_FUNCTION_SINGLE: generic_smp_call_function_single_interrupt(); break; + case SMP_MSG_TIMER: + ipi_timer(); + break; default: printk(KERN_WARNING "SMP %d: %s(): unknown IPI %d\n", smp_processor_id(), __func__, msg); -- cgit v1.2.3 From 8c24594deab89a484879bee270e948f0a556ed75 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 6 Aug 2008 18:37:07 +0900 Subject: sh: generic clockevent broadcast support. This hooks up GENERIC_CLOCKEVENTS_BROADCAST and a dummy local timer, which we call in to from the timer IPI when no other local timer is provided. Signed-off-by: Paul Mundt --- arch/sh/Kconfig | 4 +++ arch/sh/include/asm/smp.h | 3 ++ arch/sh/kernel/smp.c | 7 ++-- arch/sh/kernel/time_32.c | 9 ++++-- arch/sh/kernel/timers/Makefile | 1 + arch/sh/kernel/timers/timer-broadcast.c | 57 +++++++++++++++++++++++++++++++++ 6 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 arch/sh/kernel/timers/timer-broadcast.c (limited to 'arch/sh/include/asm') diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 5131d50f851a..399664c1f386 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -66,6 +66,9 @@ config GENERIC_TIME config GENERIC_CLOCKEVENTS def_bool n +config GENERIC_CLOCKEVENTS_BROADCAST + bool + config GENERIC_LOCKBREAK def_bool y depends on SMP && PREEMPT @@ -323,6 +326,7 @@ config CPU_SUBTYPE_SHX3 select ARCH_SPARSEMEM_ENABLE select SYS_SUPPORTS_NUMA select SYS_SUPPORTS_SMP + select GENERIC_CLOCKEVENTS_BROADCAST # SH4AL-DSP Processor Support diff --git a/arch/sh/include/asm/smp.h b/arch/sh/include/asm/smp.h index 9d22cda67c29..85b660c17eb0 100644 --- a/arch/sh/include/asm/smp.h +++ b/arch/sh/include/asm/smp.h @@ -33,6 +33,9 @@ enum { void smp_message_recv(unsigned int msg); void smp_timer_broadcast(cpumask_t mask); +void local_timer_interrupt(void); +void local_timer_setup(unsigned int cpu); + void plat_smp_setup(void); void plat_prepare_cpus(unsigned int max_cpus); int plat_smp_processor_id(void); diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c index 9cb3734dbd49..c55d314166c7 100644 --- a/arch/sh/kernel/smp.c +++ b/arch/sh/kernel/smp.c @@ -84,9 +84,12 @@ asmlinkage void __cpuinit start_secondary(void) local_irq_enable(); + cpu = smp_processor_id(); + + /* Enable local timers */ + local_timer_setup(cpu); calibrate_delay(); - cpu = smp_processor_id(); smp_store_cpu_info(cpu); cpu_set(cpu, cpu_online_map); @@ -195,7 +198,7 @@ void smp_timer_broadcast(cpumask_t mask) static void ipi_timer(void) { irq_enter(); - /* XXX ... */ + local_timer_interrupt(); irq_exit(); } diff --git a/arch/sh/kernel/time_32.c b/arch/sh/kernel/time_32.c index 0758b5ee8180..decee0a453ab 100644 --- a/arch/sh/kernel/time_32.c +++ b/arch/sh/kernel/time_32.c @@ -1,9 +1,9 @@ /* - * arch/sh/kernel/time.c + * arch/sh/kernel/time_32.c * * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka * Copyright (C) 2000 Philipp Rumpf - * Copyright (C) 2002 - 2007 Paul Mundt + * Copyright (C) 2002 - 2008 Paul Mundt * Copyright (C) 2002 M. R. Brown * * Some code taken from i386 version. @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -260,6 +261,10 @@ void __init time_init(void) sys_timer = get_sys_timer(); printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); +#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST + local_timer_setup(smp_processor_id()); +#endif + if (sys_timer->ops->read) clocksource_sh.read = sys_timer->ops->read; diff --git a/arch/sh/kernel/timers/Makefile b/arch/sh/kernel/timers/Makefile index bcf244ff6a12..0b7f8577193f 100644 --- a/arch/sh/kernel/timers/Makefile +++ b/arch/sh/kernel/timers/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_SH_TMU) += timer-tmu.o obj-$(CONFIG_SH_MTU2) += timer-mtu2.o obj-$(CONFIG_SH_CMT) += timer-cmt.o +obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) += timer-broadcast.o diff --git a/arch/sh/kernel/timers/timer-broadcast.c b/arch/sh/kernel/timers/timer-broadcast.c new file mode 100644 index 000000000000..c2317635230f --- /dev/null +++ b/arch/sh/kernel/timers/timer-broadcast.c @@ -0,0 +1,57 @@ +/* + * Dummy local timer + * + * Copyright (C) 2008 Paul Mundt + * + * cloned from: + * + * linux/arch/arm/mach-realview/localtimer.c + * + * Copyright (C) 2002 ARM Ltd. + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_PER_CPU(struct clock_event_device, local_clockevent); + +/* + * Used on SMP for either the local timer or SMP_MSG_TIMER + */ +void local_timer_interrupt(void) +{ + struct clock_event_device *clk = &__get_cpu_var(local_clockevent); + + clk->event_handler(clk); +} + +static void dummy_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ +} + +void __cpuinit local_timer_setup(unsigned int cpu) +{ + struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); + + clk->name = "dummy_timer"; + clk->features = CLOCK_EVT_FEAT_DUMMY; + clk->rating = 200; + clk->mult = 1; + clk->set_mode = dummy_timer_set_mode; + clk->broadcast = smp_timer_broadcast; + clk->cpumask = cpumask_of_cpu(cpu); + + clockevents_register_device(clk); +} -- cgit v1.2.3 From fa43972fab24a3c050e880a7831f9378c6cebc0b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 4 Sep 2008 18:53:58 +0900 Subject: sh: fixup many sparse errors. Signed-off-by: Paul Mundt --- arch/sh/boot/compressed/misc_32.c | 2 +- arch/sh/include/asm/clock.h | 1 + arch/sh/include/asm/io.h | 4 +-- arch/sh/include/asm/irq.h | 3 ++ arch/sh/include/asm/processor.h | 4 +++ arch/sh/include/asm/processor_32.h | 3 ++ arch/sh/include/asm/processor_64.h | 2 -- arch/sh/include/asm/rtc.h | 1 + arch/sh/include/asm/setup.h | 2 ++ arch/sh/include/asm/syscalls.h | 25 ++++++++++++++ arch/sh/include/asm/syscalls_32.h | 56 +++++++++++++++++++++++++++++++ arch/sh/include/asm/syscalls_64.h | 34 +++++++++++++++++++ arch/sh/include/asm/system.h | 6 ++-- arch/sh/include/asm/system_32.h | 16 +++++++++ arch/sh/kernel/cpu/clock.c | 5 +-- arch/sh/kernel/io_generic.c | 6 ++-- arch/sh/kernel/machvec.c | 1 + arch/sh/kernel/process_32.c | 1 + arch/sh/kernel/process_64.c | 1 + arch/sh/kernel/ptrace_32.c | 6 ++-- arch/sh/kernel/ptrace_64.c | 1 + arch/sh/kernel/setup.c | 1 + arch/sh/kernel/signal_32.c | 10 +++--- arch/sh/kernel/sys_sh.c | 7 ++-- arch/sh/kernel/sys_sh32.c | 5 +-- arch/sh/kernel/time_32.c | 1 + arch/sh/kernel/timers/timer-cmt.c | 2 +- arch/sh/kernel/traps_32.c | 68 ++++++++++++++++++++------------------ arch/sh/lib/div64-generic.c | 1 + arch/sh/mm/consistent.c | 2 +- arch/sh/mm/pg-nommu.c | 1 + arch/sh/mm/tlb-nommu.c | 1 + drivers/serial/sh-sci.c | 2 +- 33 files changed, 220 insertions(+), 61 deletions(-) create mode 100644 arch/sh/include/asm/syscalls.h create mode 100644 arch/sh/include/asm/syscalls_32.h create mode 100644 arch/sh/include/asm/syscalls_64.h (limited to 'arch/sh/include/asm') diff --git a/arch/sh/boot/compressed/misc_32.c b/arch/sh/boot/compressed/misc_32.c index f386997e4d9c..efdba6b29572 100644 --- a/arch/sh/boot/compressed/misc_32.c +++ b/arch/sh/boot/compressed/misc_32.c @@ -191,7 +191,7 @@ long* stack_start = &user_stack[STACK_SIZE]; void decompress_kernel(void) { - output_data = 0; + output_data = NULL; output_ptr = PHYSADDR((unsigned long)&_text+PAGE_SIZE); #ifdef CONFIG_29BIT output_ptr |= P2SEG; diff --git a/arch/sh/include/asm/clock.h b/arch/sh/include/asm/clock.h index 720dfab7b15e..f9c88583d90a 100644 --- a/arch/sh/include/asm/clock.h +++ b/arch/sh/include/asm/clock.h @@ -39,6 +39,7 @@ struct clk { /* Should be defined by processor-specific code */ void arch_init_clk_ops(struct clk_ops **, int type); +int __init arch_clk_init(void); /* arch/sh/kernel/cpu/clock.c */ int clk_init(void); diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h index a4fbf0c84fb1..e49cfee95fb2 100644 --- a/arch/sh/include/asm/io.h +++ b/arch/sh/include/asm/io.h @@ -194,6 +194,8 @@ __BUILD_MEMORY_STRING(w, u16) #define IO_SPACE_LIMIT 0xffffffff +extern unsigned long generic_io_base; + /* * This function provides a method for the generic case where a board-specific * ioport_map simply needs to return the port + some arbitrary port base. @@ -203,8 +205,6 @@ __BUILD_MEMORY_STRING(w, u16) */ static inline void __set_io_port_base(unsigned long pbase) { - extern unsigned long generic_io_base; - generic_io_base = pbase; } diff --git a/arch/sh/include/asm/irq.h b/arch/sh/include/asm/irq.h index 6195a531c1b0..d319baaf4fbd 100644 --- a/arch/sh/include/asm/irq.h +++ b/arch/sh/include/asm/irq.h @@ -41,6 +41,9 @@ static inline int generic_irq_demux(int irq) #define irq_canonicalize(irq) (irq) #define irq_demux(irq) sh_mv.mv_irq_demux(irq) +void init_IRQ(void); +asmlinkage int do_IRQ(unsigned int irq, struct pt_regs *regs); + #ifdef CONFIG_IRQSTACKS extern void irq_ctx_init(int cpu); extern void irq_ctx_exit(int cpu); diff --git a/arch/sh/include/asm/processor.h b/arch/sh/include/asm/processor.h index 15d9f92ca383..58e2be55ab93 100644 --- a/arch/sh/include/asm/processor.h +++ b/arch/sh/include/asm/processor.h @@ -45,9 +45,13 @@ enum cpu_type { /* Forward decl */ struct sh_cpuinfo; +struct seq_operations; + +extern struct pt_regs fake_swapper_regs; /* arch/sh/kernel/setup.c */ const char *get_cpu_subtype(struct sh_cpuinfo *c); +extern const struct seq_operations cpuinfo_op; #ifdef CONFIG_VSYSCALL int vsyscall_init(void); diff --git a/arch/sh/include/asm/processor_32.h b/arch/sh/include/asm/processor_32.h index 0dadd75bd93c..41d23210583c 100644 --- a/arch/sh/include/asm/processor_32.h +++ b/arch/sh/include/asm/processor_32.h @@ -10,6 +10,7 @@ #ifdef __KERNEL__ #include +#include #include #include #include @@ -44,6 +45,8 @@ extern struct sh_cpuinfo cpu_data[]; #define current_cpu_data cpu_data[smp_processor_id()] #define raw_current_cpu_data cpu_data[raw_smp_processor_id()] +asmlinkage void __init sh_cpu_init(void); + /* * User space process size: 2GB. * diff --git a/arch/sh/include/asm/processor_64.h b/arch/sh/include/asm/processor_64.h index 770d5169983b..16609bc11627 100644 --- a/arch/sh/include/asm/processor_64.h +++ b/arch/sh/include/asm/processor_64.h @@ -169,8 +169,6 @@ struct thread_struct { #define INIT_MMAP \ { &init_mm, 0, 0, NULL, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL } -extern struct pt_regs fake_swapper_regs; - #define INIT_THREAD { \ .sp = sizeof(init_stack) + \ (long) &init_stack, \ diff --git a/arch/sh/include/asm/rtc.h b/arch/sh/include/asm/rtc.h index 1813f4202a24..f7b010d48af7 100644 --- a/arch/sh/include/asm/rtc.h +++ b/arch/sh/include/asm/rtc.h @@ -1,6 +1,7 @@ #ifndef _ASM_RTC_H #define _ASM_RTC_H +void time_init(void); extern void (*board_time_init)(void); extern void (*rtc_sh_get_time)(struct timespec *); extern int (*rtc_sh_set_time)(const time_t); diff --git a/arch/sh/include/asm/setup.h b/arch/sh/include/asm/setup.h index 55a2bd328d99..554f865075ca 100644 --- a/arch/sh/include/asm/setup.h +++ b/arch/sh/include/asm/setup.h @@ -1,6 +1,8 @@ #ifndef _SH_SETUP_H #define _SH_SETUP_H +#include + #define COMMAND_LINE_SIZE 256 #ifdef __KERNEL__ diff --git a/arch/sh/include/asm/syscalls.h b/arch/sh/include/asm/syscalls.h new file mode 100644 index 000000000000..c1e2b8deb837 --- /dev/null +++ b/arch/sh/include/asm/syscalls.h @@ -0,0 +1,25 @@ +#ifndef __ASM_SH_SYSCALLS_H +#define __ASM_SH_SYSCALLS_H + +#ifdef __KERNEL__ + +struct old_utsname; + +asmlinkage int old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + int fd, unsigned long off); +asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff); +asmlinkage int sys_ipc(uint call, int first, int second, + int third, void __user *ptr, long fifth); +asmlinkage int sys_uname(struct old_utsname __user *name); + +#ifdef CONFIG_SUPERH32 +# include "syscalls_32.h" +#else +# include "syscalls_64.h" +#endif + +#endif /* __KERNEL__ */ +#endif /* __ASM_SH_SYSCALLS_H */ diff --git a/arch/sh/include/asm/syscalls_32.h b/arch/sh/include/asm/syscalls_32.h new file mode 100644 index 000000000000..104c5e686106 --- /dev/null +++ b/arch/sh/include/asm/syscalls_32.h @@ -0,0 +1,56 @@ +#ifndef __ASM_SH_SYSCALLS_32_H +#define __ASM_SH_SYSCALLS_32_H + +#ifdef __KERNEL__ + +#include +#include +#include + +struct pt_regs; + +asmlinkage int sys_fork(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs); +asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, + unsigned long parent_tidptr, + unsigned long child_tidptr, + struct pt_regs __regs); +asmlinkage int sys_vfork(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs); +asmlinkage int sys_execve(char __user *ufilename, char __user * __user *uargv, + char __user * __user *uenvp, unsigned long r7, + struct pt_regs __regs); +asmlinkage int sys_sigsuspend(old_sigset_t mask, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs); +asmlinkage int sys_sigaction(int sig, const struct old_sigaction __user *act, + struct old_sigaction __user *oact); +asmlinkage int sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, + unsigned long r6, unsigned long r7, + struct pt_regs __regs); +asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs); +asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs); +asmlinkage int sys_pipe(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs); +asmlinkage ssize_t sys_pread_wrapper(unsigned int fd, char __user *buf, + size_t count, long dummy, loff_t pos); +asmlinkage ssize_t sys_pwrite_wrapper(unsigned int fd, const char __user *buf, + size_t count, long dummy, loff_t pos); +asmlinkage int sys_fadvise64_64_wrapper(int fd, u32 offset0, u32 offset1, + u32 len0, u32 len1, int advice); + +/* Misc syscall related bits */ +asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); +asmlinkage void do_syscall_trace_leave(struct pt_regs *regs); +asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int save_r0, + unsigned long thread_info_flags); + +#endif /* __KERNEL__ */ +#endif /* __ASM_SH_SYSCALLS_32_H */ diff --git a/arch/sh/include/asm/syscalls_64.h b/arch/sh/include/asm/syscalls_64.h new file mode 100644 index 000000000000..751fd8811364 --- /dev/null +++ b/arch/sh/include/asm/syscalls_64.h @@ -0,0 +1,34 @@ +#ifndef __ASM_SH_SYSCALLS_64_H +#define __ASM_SH_SYSCALLS_64_H + +#ifdef __KERNEL__ + +#include +#include +#include + +struct pt_regs; + +asmlinkage int sys_fork(unsigned long r2, unsigned long r3, + unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs *pregs); +asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, + unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs *pregs); +asmlinkage int sys_vfork(unsigned long r2, unsigned long r3, + unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs *pregs); +asmlinkage int sys_execve(char *ufilename, char **uargv, + char **uenvp, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs *pregs); + +/* Misc syscall related bits */ +asmlinkage long long do_syscall_trace_enter(struct pt_regs *regs); +asmlinkage void do_syscall_trace_leave(struct pt_regs *regs); + +#endif /* __KERNEL__ */ +#endif /* __ASM_SH_SYSCALLS_64_H */ diff --git a/arch/sh/include/asm/system.h b/arch/sh/include/asm/system.h index fbac113bbfbf..6160fe445161 100644 --- a/arch/sh/include/asm/system.h +++ b/arch/sh/include/asm/system.h @@ -127,6 +127,8 @@ static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old, }) extern void die(const char *str, struct pt_regs *regs, long err) __attribute__ ((noreturn)); +void free_initmem(void); +void free_initrd_mem(unsigned long start, unsigned long end); extern void *set_exception_table_vec(unsigned int vec, void *handler); @@ -179,8 +181,8 @@ BUILD_TRAP_HANDLER(fpu_state_restore); #define arch_align_stack(x) (x) struct mem_access { - unsigned long (*from)(void *dst, const void *src, unsigned long cnt); - unsigned long (*to)(void *dst, const void *src, unsigned long cnt); + unsigned long (*from)(void *dst, const void __user *src, unsigned long cnt); + unsigned long (*to)(void __user *dst, const void *src, unsigned long cnt); }; #ifdef CONFIG_SUPERH32 diff --git a/arch/sh/include/asm/system_32.h b/arch/sh/include/asm/system_32.h index f11bcf0855ed..16509ed2bb60 100644 --- a/arch/sh/include/asm/system_32.h +++ b/arch/sh/include/asm/system_32.h @@ -99,4 +99,20 @@ do { \ int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs, struct mem_access *ma); +asmlinkage void do_address_error(struct pt_regs *regs, + unsigned long writeaccess, + unsigned long address); +asmlinkage void do_divide_error(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs); +asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs); +asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs); +asmlinkage void do_exception_error(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs __regs); + #endif /* __ASM_SH_SYSTEM_32_H */ diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c index f5eb56e6bc59..b7e46d5bba43 100644 --- a/arch/sh/kernel/cpu/clock.c +++ b/arch/sh/kernel/cpu/clock.c @@ -294,9 +294,10 @@ arch_init_clk_ops(struct clk_ops **ops, int type) { } -void __init __attribute__ ((weak)) +int __init __attribute__ ((weak)) arch_clk_init(void) { + return 0; } static int show_clocks(char *buf, char **start, off_t off, @@ -331,7 +332,7 @@ int __init clk_init(void) ret |= clk_register(clk); } - arch_clk_init(); + ret |= arch_clk_init(); /* Kick the child clocks.. */ propagate_rate(&master_clk); diff --git a/arch/sh/kernel/io_generic.c b/arch/sh/kernel/io_generic.c index db769449f5a7..f1b214d3bce3 100644 --- a/arch/sh/kernel/io_generic.c +++ b/arch/sh/kernel/io_generic.c @@ -81,7 +81,7 @@ void generic_insb(unsigned long port, void *dst, unsigned long count) volatile u8 *port_addr; u8 *buf = dst; - port_addr = (volatile u8 *)__ioport_map(port, 1); + port_addr = (volatile u8 __force *)__ioport_map(port, 1); while (count--) *buf++ = *port_addr; } @@ -91,7 +91,7 @@ void generic_insw(unsigned long port, void *dst, unsigned long count) volatile u16 *port_addr; u16 *buf = dst; - port_addr = (volatile u16 *)__ioport_map(port, 2); + port_addr = (volatile u16 __force *)__ioport_map(port, 2); while (count--) *buf++ = *port_addr; @@ -103,7 +103,7 @@ void generic_insl(unsigned long port, void *dst, unsigned long count) volatile u32 *port_addr; u32 *buf = dst; - port_addr = (volatile u32 *)__ioport_map(port, 4); + port_addr = (volatile u32 __force *)__ioport_map(port, 4); while (count--) *buf++ = *port_addr; diff --git a/arch/sh/kernel/machvec.c b/arch/sh/kernel/machvec.c index 129b2cfd18a8..8bfdd275e940 100644 --- a/arch/sh/kernel/machvec.c +++ b/arch/sh/kernel/machvec.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c index 7326313bcfdb..914e543102df 100644 --- a/arch/sh/kernel/process_32.c +++ b/arch/sh/kernel/process_32.c @@ -26,6 +26,7 @@ #include #include #include +#include static int hlt_counter; int ubc_usercnt = 0; diff --git a/arch/sh/kernel/process_64.c b/arch/sh/kernel/process_64.c index b9dbd2d3b4a5..d0dddc438c0c 100644 --- a/arch/sh/kernel/process_64.c +++ b/arch/sh/kernel/process_64.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 035cb300d3dc..84bf3420597c 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -27,6 +27,7 @@ #include #include #include +#include /* * does not yet catch signals sent when the child dies. @@ -105,6 +106,7 @@ void ptrace_disable(struct task_struct *child) long arch_ptrace(struct task_struct *child, long request, long addr, long data) { struct user * dummy = NULL; + unsigned long __user *datap = (unsigned long __user *)data; int ret; switch (request) { @@ -133,7 +135,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) tmp = !!tsk_used_math(child); else tmp = 0; - ret = put_user(tmp, (unsigned long __user *)data); + ret = put_user(tmp, datap); break; } @@ -202,7 +204,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) } ret = 0; - if (put_user(tmp, (unsigned long *) data)) { + if (put_user(tmp, datap)) { ret = -EFAULT; break; } diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c index 9c6424892bd3..e15b099c1f06 100644 --- a/arch/sh/kernel/ptrace_64.c +++ b/arch/sh/kernel/ptrace_64.c @@ -35,6 +35,7 @@ #include #include #include +#include #include /* This mask defines the bits of the SR which the user is not allowed to diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index de832056bf1b..6d0899e890d0 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c index 51689d29ad45..345de2f1d53c 100644 --- a/arch/sh/kernel/signal_32.c +++ b/arch/sh/kernel/signal_32.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) @@ -247,7 +248,6 @@ asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, struct pt_regs *regs = RELOC_HIDE(&__regs, 0); struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs->regs[15]; sigset_t set; - stack_t st; int r0; if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) @@ -265,11 +265,9 @@ asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &r0)) goto badframe; - if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + if (do_sigaltstack(&frame->uc.uc_stack, NULL, + regs->regs[15]) == -EFAULT) goto badframe; - /* It is more difficult to avoid calling this function than to - call it and ignore errors. */ - do_sigaltstack((const stack_t __user *)&st, NULL, (unsigned long)frame); return r0; @@ -429,7 +427,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(NULL, &frame->uc.uc_link); err |= __put_user((void *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); err |= __put_user(sas_ss_flags(regs->regs[15]), diff --git a/arch/sh/kernel/sys_sh.c b/arch/sh/kernel/sys_sh.c index 9061b86d73fa..0dfb88925add 100644 --- a/arch/sh/kernel/sys_sh.c +++ b/arch/sh/kernel/sys_sh.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -186,7 +187,7 @@ asmlinkage int sys_ipc(uint call, int first, int second, union semun fourth; if (!ptr) return -EINVAL; - if (get_user(fourth.__pad, (void * __user *) ptr)) + if (get_user(fourth.__pad, (void __user * __user *) ptr)) return -EFAULT; return sys_semctl (first, second, third, fourth); } @@ -261,13 +262,13 @@ asmlinkage int sys_ipc(uint call, int first, int second, return -EINVAL; } -asmlinkage int sys_uname(struct old_utsname * name) +asmlinkage int sys_uname(struct old_utsname __user *name) { int err; if (!name) return -EFAULT; down_read(&uts_sem); - err = copy_to_user(name, utsname(), sizeof (*name)); + err = copy_to_user(name, utsname(), sizeof(*name)); up_read(&uts_sem); return err?-EFAULT:0; } diff --git a/arch/sh/kernel/sys_sh32.c b/arch/sh/kernel/sys_sh32.c index f0aa5c398656..dbba1e1833d4 100644 --- a/arch/sh/kernel/sys_sh32.c +++ b/arch/sh/kernel/sys_sh32.c @@ -16,6 +16,7 @@ #include #include #include +#include /* * sys_pipe() is the normal C calling standard for creating @@ -37,13 +38,13 @@ asmlinkage int sys_pipe(unsigned long r4, unsigned long r5, return error; } -asmlinkage ssize_t sys_pread_wrapper(unsigned int fd, char * buf, +asmlinkage ssize_t sys_pread_wrapper(unsigned int fd, char __user *buf, size_t count, long dummy, loff_t pos) { return sys_pread64(fd, buf, count, pos); } -asmlinkage ssize_t sys_pwrite_wrapper(unsigned int fd, const char * buf, +asmlinkage ssize_t sys_pwrite_wrapper(unsigned int fd, const char __user *buf, size_t count, long dummy, loff_t pos) { return sys_pwrite64(fd, buf, count, pos); diff --git a/arch/sh/kernel/time_32.c b/arch/sh/kernel/time_32.c index e2f74cc71d8c..23ca711c27d2 100644 --- a/arch/sh/kernel/time_32.c +++ b/arch/sh/kernel/time_32.c @@ -16,6 +16,7 @@ #include #include #include +#include /* for rtc_lock */ #include #include #include diff --git a/arch/sh/kernel/timers/timer-cmt.c b/arch/sh/kernel/timers/timer-cmt.c index d20c8c375881..c127293271e1 100644 --- a/arch/sh/kernel/timers/timer-cmt.c +++ b/arch/sh/kernel/timers/timer-cmt.c @@ -174,7 +174,7 @@ static int cmt_timer_init(void) return 0; } -struct sys_timer_ops cmt_timer_ops = { +static struct sys_timer_ops cmt_timer_ops = { .init = cmt_timer_init, .start = cmt_timer_start, .stop = cmt_timer_stop, diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 511a9426cec5..4901f6732162 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -192,6 +192,7 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs, int ret, index, count; unsigned long *rm, *rn; unsigned char *src, *dst; + unsigned char __user *srcu, *dstu; index = (instruction>>8)&15; /* 0x0F00 */ rn = ®s->regs[index]; @@ -206,28 +207,28 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs, case 0: /* mov.[bwl] to/from memory via r0+rn */ if (instruction & 8) { /* from memory */ - src = (unsigned char*) *rm; - src += regs->regs[0]; - dst = (unsigned char*) rn; - *(unsigned long*)dst = 0; + srcu = (unsigned char __user *)*rm; + srcu += regs->regs[0]; + dst = (unsigned char *)rn; + *(unsigned long *)dst = 0; #if !defined(__LITTLE_ENDIAN__) dst += 4-count; #endif - if (ma->from(dst, src, count)) + if (ma->from(dst, srcu, count)) goto fetch_fault; sign_extend(count, dst); } else { /* to memory */ - src = (unsigned char*) rm; + src = (unsigned char *)rm; #if !defined(__LITTLE_ENDIAN__) src += 4-count; #endif - dst = (unsigned char*) *rn; - dst += regs->regs[0]; + dstu = (unsigned char __user *)*rn; + dstu += regs->regs[0]; - if (ma->to(dst, src, count)) + if (ma->to(dstu, src, count)) goto fetch_fault; } ret = 0; @@ -235,10 +236,10 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs, case 1: /* mov.l Rm,@(disp,Rn) */ src = (unsigned char*) rm; - dst = (unsigned char*) *rn; - dst += (instruction&0x000F)<<2; + dstu = (unsigned char __user *)*rn; + dstu += (instruction&0x000F)<<2; - if (ma->to(dst, src, 4)) + if (ma->to(dstu, src, 4)) goto fetch_fault; ret = 0; break; @@ -247,28 +248,28 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs, if (instruction & 4) *rn -= count; src = (unsigned char*) rm; - dst = (unsigned char*) *rn; + dstu = (unsigned char __user *)*rn; #if !defined(__LITTLE_ENDIAN__) src += 4-count; #endif - if (ma->to(dst, src, count)) + if (ma->to(dstu, src, count)) goto fetch_fault; ret = 0; break; case 5: /* mov.l @(disp,Rm),Rn */ - src = (unsigned char*) *rm; - src += (instruction&0x000F)<<2; - dst = (unsigned char*) rn; - *(unsigned long*)dst = 0; + srcu = (unsigned char __user *)*rm; + srcu += (instruction & 0x000F) << 2; + dst = (unsigned char *)rn; + *(unsigned long *)dst = 0; - if (ma->from(dst, src, 4)) + if (ma->from(dst, srcu, 4)) goto fetch_fault; ret = 0; break; case 6: /* mov.[bwl] from memory, possibly with post-increment */ - src = (unsigned char*) *rm; + srcu = (unsigned char __user *)*rm; if (instruction & 4) *rm += count; dst = (unsigned char*) rn; @@ -277,7 +278,7 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs, #if !defined(__LITTLE_ENDIAN__) dst += 4-count; #endif - if (ma->from(dst, src, count)) + if (ma->from(dst, srcu, count)) goto fetch_fault; sign_extend(count, dst); ret = 0; @@ -286,28 +287,28 @@ static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs, case 8: switch ((instruction&0xFF00)>>8) { case 0x81: /* mov.w R0,@(disp,Rn) */ - src = (unsigned char*) ®s->regs[0]; + src = (unsigned char *) ®s->regs[0]; #if !defined(__LITTLE_ENDIAN__) src += 2; #endif - dst = (unsigned char*) *rm; /* called Rn in the spec */ - dst += (instruction&0x000F)<<1; + dstu = (unsigned char __user *)*rm; /* called Rn in the spec */ + dstu += (instruction & 0x000F) << 1; - if (ma->to(dst, src, 2)) + if (ma->to(dstu, src, 2)) goto fetch_fault; ret = 0; break; case 0x85: /* mov.w @(disp,Rm),R0 */ - src = (unsigned char*) *rm; - src += (instruction&0x000F)<<1; - dst = (unsigned char*) ®s->regs[0]; - *(unsigned long*)dst = 0; + srcu = (unsigned char __user *)*rm; + srcu += (instruction & 0x000F) << 1; + dst = (unsigned char *) ®s->regs[0]; + *(unsigned long *)dst = 0; #if !defined(__LITTLE_ENDIAN__) dst += 2; #endif - if (ma->from(dst, src, 2)) + if (ma->from(dst, srcu, 2)) goto fetch_fault; sign_extend(2, dst); ret = 0; @@ -333,7 +334,8 @@ static inline int handle_delayslot(struct pt_regs *regs, struct mem_access *ma) { opcode_t instruction; - void *addr = (void *)(regs->pc + instruction_size(old_instruction)); + void __user *addr = (void __user *)(regs->pc + + instruction_size(old_instruction)); if (copy_from_user(&instruction, addr, sizeof(instruction))) { /* the instruction-fetch faulted */ @@ -559,7 +561,7 @@ asmlinkage void do_address_error(struct pt_regs *regs, } set_fs(USER_DS); - if (copy_from_user(&instruction, (void *)(regs->pc), + if (copy_from_user(&instruction, (void __user *)(regs->pc), sizeof(instruction))) { /* Argh. Fault on the instruction itself. This should never happen non-SMP @@ -589,7 +591,7 @@ uspace_segv: die("unaligned program counter", regs, error_code); set_fs(KERNEL_DS); - if (copy_from_user(&instruction, (void *)(regs->pc), + if (copy_from_user(&instruction, (void __user *)(regs->pc), sizeof(instruction))) { /* Argh. Fault on the instruction itself. This should never happen non-SMP diff --git a/arch/sh/lib/div64-generic.c b/arch/sh/lib/div64-generic.c index 4bef3b5d964a..60e76aa8b53e 100644 --- a/arch/sh/lib/div64-generic.c +++ b/arch/sh/lib/div64-generic.c @@ -3,6 +3,7 @@ */ #include +#include extern uint64_t __xdiv64_32(u64 n, u32 d); diff --git a/arch/sh/mm/consistent.c b/arch/sh/mm/consistent.c index 64b8f7f96f9a..7619a0fae086 100644 --- a/arch/sh/mm/consistent.c +++ b/arch/sh/mm/consistent.c @@ -44,7 +44,7 @@ void *dma_alloc_coherent(struct device *dev, size_t size, */ dma_cache_sync(dev, ret, size, DMA_BIDIRECTIONAL); - ret_nocache = ioremap_nocache(virt_to_phys(ret), size); + ret_nocache = (void __force *)ioremap_nocache(virt_to_phys(ret), size); if (!ret_nocache) { free_pages((unsigned long)ret, order); return NULL; diff --git a/arch/sh/mm/pg-nommu.c b/arch/sh/mm/pg-nommu.c index 677dd57f0877..91ed4e695ff7 100644 --- a/arch/sh/mm/pg-nommu.c +++ b/arch/sh/mm/pg-nommu.c @@ -13,6 +13,7 @@ #include #include #include +#include void copy_page(void *to, void *from) { diff --git a/arch/sh/mm/tlb-nommu.c b/arch/sh/mm/tlb-nommu.c index 15111bc7ddd6..71c742b5aee3 100644 --- a/arch/sh/mm/tlb-nommu.c +++ b/arch/sh/mm/tlb-nommu.c @@ -10,6 +10,7 @@ #include #include #include +#include /* * Nothing too terribly exciting here .. diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 3df2aaec829f..f5aebc9f27e7 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -1113,7 +1113,7 @@ static const char *sci_type(struct uart_port *port) case PORT_IRDA: return "irda"; } - return 0; + return NULL; } static void sci_release_port(struct uart_port *port) -- cgit v1.2.3 From c6feb6142cb85228e73497a309f475a0d7279318 Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Fri, 5 Sep 2008 16:06:42 +0900 Subject: sh: early cached_to_uncached initialization. statically initialise the cached_to_uncached offset, so that we can use it immediatly. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/include/asm/system_32.h | 3 ++- arch/sh/mm/init.c | 27 ++++++++++++++------------- 2 files changed, 16 insertions(+), 14 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/system_32.h b/arch/sh/include/asm/system_32.h index 16509ed2bb60..f7f105627fd9 100644 --- a/arch/sh/include/asm/system_32.h +++ b/arch/sh/include/asm/system_32.h @@ -58,7 +58,8 @@ do { \ last = __last; \ } while (0) -#define __uses_jump_to_uncached __attribute__ ((__section__ (".uncached.text"))) +#define __uses_jump_to_uncached \ + noinline __attribute__ ((__section__ (".uncached.text"))) /* * Jump to uncached area. diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index d4681a55c852..f1a494283c4e 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -23,7 +23,19 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); pgd_t swapper_pg_dir[PTRS_PER_PGD]; -unsigned long cached_to_uncached = 0; + +#ifdef CONFIG_SUPERH32 +/* + * Handle trivial transitions between cached and uncached + * segments, making use of the 1:1 mapping relationship in + * 512MB lowmem. + * + * This is the offset of the uncached section from its cached alias. + * Default value only valid in 29 bit mode, in 32bit mode will be + * overridden in pmb_init. + */ +unsigned long cached_to_uncached = P2SEG - P1SEG; +#endif #ifdef CONFIG_MMU static void set_pte_phys(unsigned long addr, unsigned long phys, pgprot_t prot) @@ -58,9 +70,7 @@ static void set_pte_phys(unsigned long addr, unsigned long phys, pgprot_t prot) } set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, prot)); - - if (cached_to_uncached) - flush_tlb_one(get_asid(), addr); + flush_tlb_one(get_asid(), addr); } /* @@ -165,15 +175,6 @@ void __init paging_init(void) #ifdef CONFIG_SUPERH32 /* Set up the uncached fixmap */ set_fixmap_nocache(FIX_UNCACHED, __pa(&__uncached_start)); - -#ifdef CONFIG_29BIT - /* - * Handle trivial transitions between cached and uncached - * segments, making use of the 1:1 mapping relationship in - * 512MB lowmem. - */ - cached_to_uncached = P2SEG - P1SEG; -#endif #endif } -- cgit v1.2.3 From d39f5450146ff39f66cfde9d5184420627d0ac51 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 5 Sep 2008 17:15:39 +0900 Subject: sh: Add kprobes support. Initial support for kprobes/kretprobes for 32-bit SH platforms. [ General cleanup and some rework for the kretprobe hash lock. -- PFM ] Signed-off-by: Chris Smith Signed-off-by: Paul Mundt --- arch/sh/Kconfig | 2 + arch/sh/include/asm/kprobes.h | 59 +++++ arch/sh/kernel/Makefile_32 | 1 + arch/sh/kernel/kprobes.c | 568 ++++++++++++++++++++++++++++++++++++++++++ arch/sh/kernel/traps_32.c | 5 + 5 files changed, 635 insertions(+) create mode 100644 arch/sh/include/asm/kprobes.h create mode 100644 arch/sh/kernel/kprobes.c (limited to 'arch/sh/include/asm') diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index af2b174fd7d9..334917a62ecf 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -20,6 +20,8 @@ config SUPERH config SUPERH32 def_bool !SUPERH64 + select HAVE_KPROBES + select HAVE_KRETPROBES config SUPERH64 def_bool y if CPU_SH5 diff --git a/arch/sh/include/asm/kprobes.h b/arch/sh/include/asm/kprobes.h new file mode 100644 index 000000000000..70fc629df904 --- /dev/null +++ b/arch/sh/include/asm/kprobes.h @@ -0,0 +1,59 @@ +#ifndef __ASM_SH_KPROBES_H +#define __ASM_SH_KPROBES_H + +#ifdef CONFIG_KPROBES + +#include +#include + +struct pt_regs; + +typedef u16 kprobe_opcode_t; +#define BREAKPOINT_INSTRUCTION 0xc3ff + +#define MAX_INSN_SIZE 16 +#define MAX_STACK_SIZE 64 +#define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \ + (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR))) \ + ? (MAX_STACK_SIZE) \ + : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR))) + +#define regs_return_value(regs) ((regs)->regs[0]) +#define flush_insn_slot(p) do { } while (0) +#define kretprobe_blacklist_size 0 + +struct kprobe; + +void arch_remove_kprobe(struct kprobe *); +void kretprobe_trampoline(void); +void jprobe_return_end(void); + +/* Architecture specific copy of original instruction*/ +struct arch_specific_insn { + /* copy of the original instruction */ + kprobe_opcode_t insn[MAX_INSN_SIZE]; +}; + +struct prev_kprobe { + struct kprobe *kp; + unsigned long status; +}; + +/* per-cpu kprobe control block */ +struct kprobe_ctlblk { + unsigned long kprobe_status; + unsigned long jprobe_saved_r15; + struct pt_regs jprobe_saved_regs; + kprobe_opcode_t jprobes_stack[MAX_STACK_SIZE]; + struct prev_kprobe prev_kprobe; +}; + +extern int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data); +extern int kprobe_handle_illslot(unsigned long pc); +#else + +#define kprobe_handle_illslot(pc) (-1) + +#endif /* CONFIG_KPROBES */ +#endif /* __ASM_SH_KPROBES_H */ diff --git a/arch/sh/kernel/Makefile_32 b/arch/sh/kernel/Makefile_32 index 0e6905fe9fec..12e617beba20 100644 --- a/arch/sh/kernel/Makefile_32 +++ b/arch/sh/kernel/Makefile_32 @@ -23,5 +23,6 @@ obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_ELF_CORE) += dump_task.o obj-$(CONFIG_IO_TRAPPED) += io_trapped.o +obj-$(CONFIG_KPROBES) += kprobes.o EXTRA_CFLAGS += -Werror diff --git a/arch/sh/kernel/kprobes.c b/arch/sh/kernel/kprobes.c new file mode 100644 index 000000000000..c4f4a0923eb9 --- /dev/null +++ b/arch/sh/kernel/kprobes.c @@ -0,0 +1,568 @@ +/* + * Kernel probes (kprobes) for SuperH + * + * Copyright (C) 2007 Chris Smith + * Copyright (C) 2006 Lineo Solutions, Inc. + * + * 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 +#include +#include +#include +#include +#include + +DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; +DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); + +static struct kprobe saved_current_opcode; +static struct kprobe saved_next_opcode; +static struct kprobe saved_next_opcode2; + +#define OPCODE_JMP(x) (((x) & 0xF0FF) == 0x402b) +#define OPCODE_JSR(x) (((x) & 0xF0FF) == 0x400b) +#define OPCODE_BRA(x) (((x) & 0xF000) == 0xa000) +#define OPCODE_BRAF(x) (((x) & 0xF0FF) == 0x0023) +#define OPCODE_BSR(x) (((x) & 0xF000) == 0xb000) +#define OPCODE_BSRF(x) (((x) & 0xF0FF) == 0x0003) + +#define OPCODE_BF_S(x) (((x) & 0xFF00) == 0x8f00) +#define OPCODE_BT_S(x) (((x) & 0xFF00) == 0x8d00) + +#define OPCODE_BF(x) (((x) & 0xFF00) == 0x8b00) +#define OPCODE_BT(x) (((x) & 0xFF00) == 0x8900) + +#define OPCODE_RTS(x) (((x) & 0x000F) == 0x000b) +#define OPCODE_RTE(x) (((x) & 0xFFFF) == 0x002b) + +int __kprobes arch_prepare_kprobe(struct kprobe *p) +{ + kprobe_opcode_t opcode = *(kprobe_opcode_t *) (p->addr); + + if (OPCODE_RTE(opcode)) + return -EFAULT; /* Bad breakpoint */ + + p->opcode = opcode; + + return 0; +} + +void __kprobes arch_copy_kprobe(struct kprobe *p) +{ + memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); + p->opcode = *p->addr; +} + +void __kprobes arch_arm_kprobe(struct kprobe *p) +{ + *p->addr = BREAKPOINT_INSTRUCTION; + flush_icache_range((unsigned long)p->addr, + (unsigned long)p->addr + sizeof(kprobe_opcode_t)); +} + +void __kprobes arch_disarm_kprobe(struct kprobe *p) +{ + *p->addr = p->opcode; + flush_icache_range((unsigned long)p->addr, + (unsigned long)p->addr + sizeof(kprobe_opcode_t)); +} + +int __kprobes arch_trampoline_kprobe(struct kprobe *p) +{ + if (*p->addr == BREAKPOINT_INSTRUCTION) + return 1; + + return 0; +} + +/** + * If an illegal slot instruction exception occurs for an address + * containing a kprobe, remove the probe. + * + * Returns 0 if the exception was handled successfully, 1 otherwise. + */ +int __kprobes kprobe_handle_illslot(unsigned long pc) +{ + struct kprobe *p = get_kprobe((kprobe_opcode_t *) pc + 1); + + if (p != NULL) { + printk("Warning: removing kprobe from delay slot: 0x%.8x\n", + (unsigned int)pc + 2); + unregister_kprobe(p); + return 0; + } + + return 1; +} + +void __kprobes arch_remove_kprobe(struct kprobe *p) +{ + if (saved_next_opcode.addr != 0x0) { + arch_disarm_kprobe(p); + arch_disarm_kprobe(&saved_next_opcode); + saved_next_opcode.addr = 0x0; + saved_next_opcode.opcode = 0x0; + + if (saved_next_opcode2.addr != 0x0) { + arch_disarm_kprobe(&saved_next_opcode2); + saved_next_opcode2.addr = 0x0; + saved_next_opcode2.opcode = 0x0; + } + } +} + +static inline void save_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + kcb->prev_kprobe.kp = kprobe_running(); + kcb->prev_kprobe.status = kcb->kprobe_status; +} + +static inline void restore_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; + kcb->kprobe_status = kcb->prev_kprobe.status; +} + +static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs, + struct kprobe_ctlblk *kcb) +{ + __get_cpu_var(current_kprobe) = p; +} + +/* + * Singlestep is implemented by disabling the current kprobe and setting one + * on the next instruction, following branches. Two probes are set if the + * branch is conditional. + */ +static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) +{ + kprobe_opcode_t *addr = NULL; + saved_current_opcode.addr = (kprobe_opcode_t *) (regs->pc); + addr = saved_current_opcode.addr; + + if (p != NULL) { + arch_disarm_kprobe(p); + + if (OPCODE_JSR(p->opcode) || OPCODE_JMP(p->opcode)) { + unsigned int reg_nr = ((p->opcode >> 8) & 0x000F); + saved_next_opcode.addr = + (kprobe_opcode_t *) regs->regs[reg_nr]; + } else if (OPCODE_BRA(p->opcode) || OPCODE_BSR(p->opcode)) { + unsigned long disp = (p->opcode & 0x0FFF); + saved_next_opcode.addr = + (kprobe_opcode_t *) (regs->pc + 4 + disp * 2); + + } else if (OPCODE_BRAF(p->opcode) || OPCODE_BSRF(p->opcode)) { + unsigned int reg_nr = ((p->opcode >> 8) & 0x000F); + saved_next_opcode.addr = + (kprobe_opcode_t *) (regs->pc + 4 + + regs->regs[reg_nr]); + + } else if (OPCODE_RTS(p->opcode)) { + saved_next_opcode.addr = (kprobe_opcode_t *) regs->pr; + + } else if (OPCODE_BF(p->opcode) || OPCODE_BT(p->opcode)) { + unsigned long disp = (p->opcode & 0x00FF); + /* case 1 */ + saved_next_opcode.addr = p->addr + 1; + /* case 2 */ + saved_next_opcode2.addr = + (kprobe_opcode_t *) (regs->pc + 4 + disp * 2); + saved_next_opcode2.opcode = *(saved_next_opcode2.addr); + arch_arm_kprobe(&saved_next_opcode2); + + } else if (OPCODE_BF_S(p->opcode) || OPCODE_BT_S(p->opcode)) { + unsigned long disp = (p->opcode & 0x00FF); + /* case 1 */ + saved_next_opcode.addr = p->addr + 2; + /* case 2 */ + saved_next_opcode2.addr = + (kprobe_opcode_t *) (regs->pc + 4 + disp * 2); + saved_next_opcode2.opcode = *(saved_next_opcode2.addr); + arch_arm_kprobe(&saved_next_opcode2); + + } else { + saved_next_opcode.addr = p->addr + 1; + } + + saved_next_opcode.opcode = *(saved_next_opcode.addr); + arch_arm_kprobe(&saved_next_opcode); + } +} + +/* Called with kretprobe_lock held */ +void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + ri->ret_addr = (kprobe_opcode_t *) regs->pr; + + /* Replace the return addr with trampoline addr */ + regs->pr = (unsigned long)kretprobe_trampoline; +} + +static int __kprobes kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p; + int ret = 0; + kprobe_opcode_t *addr = NULL; + struct kprobe_ctlblk *kcb; + + /* + * We don't want to be preempted for the entire + * duration of kprobe processing + */ + preempt_disable(); + kcb = get_kprobe_ctlblk(); + + addr = (kprobe_opcode_t *) (regs->pc); + + /* Check we're not actually recursing */ + if (kprobe_running()) { + p = get_kprobe(addr); + if (p) { + if (kcb->kprobe_status == KPROBE_HIT_SS && + *p->ainsn.insn == BREAKPOINT_INSTRUCTION) { + goto no_kprobe; + } + /* We have reentered the kprobe_handler(), since + * another probe was hit while within the handler. + * We here save the original kprobes variables and + * just single step on the instruction of the new probe + * without calling any user handlers. + */ + save_previous_kprobe(kcb); + set_current_kprobe(p, regs, kcb); + kprobes_inc_nmissed_count(p); + prepare_singlestep(p, regs); + kcb->kprobe_status = KPROBE_REENTER; + return 1; + } else { + p = __get_cpu_var(current_kprobe); + if (p->break_handler && p->break_handler(p, regs)) { + goto ss_probe; + } + } + goto no_kprobe; + } + + p = get_kprobe(addr); + if (!p) { + /* Not one of ours: let kernel handle it */ + goto no_kprobe; + } + + set_current_kprobe(p, regs, kcb); + kcb->kprobe_status = KPROBE_HIT_ACTIVE; + + if (p->pre_handler && p->pre_handler(p, regs)) + /* handler has already set things up, so skip ss setup */ + return 1; + + ss_probe: + prepare_singlestep(p, regs); + kcb->kprobe_status = KPROBE_HIT_SS; + return 1; + + no_kprobe: + preempt_enable_no_resched(); + return ret; +} + +/* + * For function-return probes, init_kprobes() establishes a probepoint + * here. When a retprobed function returns, this probe is hit and + * trampoline_probe_handler() runs, calling the kretprobe's handler. + */ +void kretprobe_trampoline_holder(void) +{ + asm volatile ("kretprobe_trampoline: \n" "nop\n"); +} + +/* + * Called when we hit the probe point at kretprobe_trampoline + */ +int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct kretprobe_instance *ri = NULL; + struct hlist_head *head, empty_rp; + struct hlist_node *node, *tmp; + unsigned long flags, orig_ret_address = 0; + unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; + + INIT_HLIST_HEAD(&empty_rp); + kretprobe_hash_lock(current, &head, &flags); + + /* + * It is possible to have multiple instances associated with a given + * task either because an multiple functions in the call path + * have a return probe installed on them, and/or more then one return + * return probe was registered for a target function. + * + * We can handle this because: + * - instances are always inserted at the head of the list + * - when multiple return probes are registered for the same + * function, the first instance's ret_addr will point to the + * real return address, and all the rest will point to + * kretprobe_trampoline + */ + hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { + if (ri->task != current) + /* another task is sharing our hash bucket */ + continue; + + if (ri->rp && ri->rp->handler) { + __get_cpu_var(current_kprobe) = &ri->rp->kp; + ri->rp->handler(ri, regs); + __get_cpu_var(current_kprobe) = NULL; + } + + orig_ret_address = (unsigned long)ri->ret_addr; + recycle_rp_inst(ri, &empty_rp); + + if (orig_ret_address != trampoline_address) + /* + * This is the real return address. Any other + * instances associated with this task are for + * other calls deeper on the call stack + */ + break; + } + + kretprobe_assert(ri, orig_ret_address, trampoline_address); + + regs->pc = orig_ret_address; + kretprobe_hash_unlock(current, &flags); + + preempt_enable_no_resched(); + + hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { + hlist_del(&ri->hlist); + kfree(ri); + } + + return orig_ret_address; +} + +static inline int post_kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *cur = kprobe_running(); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + kprobe_opcode_t *addr = NULL; + struct kprobe *p = NULL; + + if (!cur) + return 0; + + if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) { + kcb->kprobe_status = KPROBE_HIT_SSDONE; + cur->post_handler(cur, regs, 0); + } + + if (saved_next_opcode.addr != 0x0) { + arch_disarm_kprobe(&saved_next_opcode); + saved_next_opcode.addr = 0x0; + saved_next_opcode.opcode = 0x0; + + addr = saved_current_opcode.addr; + saved_current_opcode.addr = 0x0; + + p = get_kprobe(addr); + arch_arm_kprobe(p); + + if (saved_next_opcode2.addr != 0x0) { + arch_disarm_kprobe(&saved_next_opcode2); + saved_next_opcode2.addr = 0x0; + saved_next_opcode2.opcode = 0x0; + } + } + + /*Restore back the original saved kprobes variables and continue. */ + if (kcb->kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(kcb); + goto out; + } + reset_current_kprobe(); + + out: + preempt_enable_no_resched(); + + return 1; +} + +static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) +{ + struct kprobe *cur = kprobe_running(); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + const struct exception_table_entry *entry; + + switch (kcb->kprobe_status) { + case KPROBE_HIT_SS: + case KPROBE_REENTER: + /* + * We are here because the instruction being single + * stepped caused a page fault. We reset the current + * kprobe, point the pc back to the probe address + * and allow the page fault handler to continue as a + * normal page fault. + */ + regs->pc = (unsigned long)cur->addr; + if (kcb->kprobe_status == KPROBE_REENTER) + restore_previous_kprobe(kcb); + else + reset_current_kprobe(); + preempt_enable_no_resched(); + break; + case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SSDONE: + /* + * We increment the nmissed count for accounting, + * we can also use npre/npostfault count for accounting + * these specific fault cases. + */ + kprobes_inc_nmissed_count(cur); + + /* + * We come here because instructions in the pre/post + * handler caused the page_fault, this could happen + * if handler tries to access user space by + * copy_from_user(), get_user() etc. Let the + * user-specified handler try to fix it first. + */ + if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) + return 1; + + /* + * In case the user-specified fault handler returned + * zero, try to fix up. + */ + if ((entry = search_exception_tables(regs->pc)) != NULL) { + regs->pc = entry->fixup; + return 1; + } + + /* + * fixup_exception() could not handle it, + * Let do_page_fault() fix it. + */ + break; + default: + break; + } + return 0; +} + +/* + * Wrapper routine to for handling exceptions. + */ +int __kprobes kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + struct kprobe *p = NULL; + struct die_args *args = (struct die_args *)data; + int ret = NOTIFY_DONE; + kprobe_opcode_t *addr = NULL; + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + addr = (kprobe_opcode_t *) (args->regs->pc); + if (val == DIE_TRAP) { + if (!kprobe_running()) { + if (kprobe_handler(args->regs)) { + ret = NOTIFY_STOP; + } else { + /* Not a kprobe trap */ + force_sig(SIGTRAP, current); + } + } else { + p = get_kprobe(addr); + if ((kcb->kprobe_status == KPROBE_HIT_SS) || + (kcb->kprobe_status == KPROBE_REENTER)) { + if (post_kprobe_handler(args->regs)) + ret = NOTIFY_STOP; + } else { + if (kprobe_handler(args->regs)) { + ret = NOTIFY_STOP; + } else { + p = __get_cpu_var(current_kprobe); + if (p->break_handler + && p->break_handler(p, args->regs)) + ret = NOTIFY_STOP; + } + } + } + } + + return ret; +} + +int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + unsigned long addr; + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + kcb->jprobe_saved_regs = *regs; + kcb->jprobe_saved_r15 = regs->regs[15]; + addr = kcb->jprobe_saved_r15; + + /* + * TBD: As Linus pointed out, gcc assumes that the callee + * owns the argument space and could overwrite it, e.g. + * tailcall optimization. So, to be absolutely safe + * we also save and restore enough stack bytes to cover + * the argument area. + */ + memcpy(kcb->jprobes_stack, (kprobe_opcode_t *) addr, + MIN_STACK_SIZE(addr)); + + regs->pc = (unsigned long)(jp->entry); + + return 1; +} + +void __kprobes jprobe_return(void) +{ + __asm("trapa #-1\n\t" "jprobe_return_end:\n\t" "nop\n\t"); + +} + +int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + u8 *addr = (u8 *) regs->pc; + unsigned long stack_addr = kcb->jprobe_saved_r15; + + if ((addr >= (u8 *) jprobe_return) + && (addr <= (u8 *) jprobe_return_end)) { + *regs = kcb->jprobe_saved_regs; + + memcpy((kprobe_opcode_t *) stack_addr, kcb->jprobes_stack, + MIN_STACK_SIZE(stack_addr)); + + kcb->kprobe_status = KPROBE_HIT_SS; + return 1; + } + return 0; +} + +static struct kprobe trampoline_p = { + .addr = (kprobe_opcode_t *) &kretprobe_trampoline, + .pre_handler = trampoline_probe_handler +}; + +int __init arch_init_kprobes(void) +{ + saved_next_opcode.addr = 0x0; + saved_next_opcode.opcode = 0x0; + + saved_current_opcode.addr = 0x0; + saved_current_opcode.opcode = 0x0; + + saved_next_opcode2.addr = 0x0; + saved_next_opcode2.opcode = 0x0; + + return register_kprobe(&trampoline_p); +} diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 4901f6732162..862667a341fd 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef CONFIG_SH_KGDB #include @@ -743,6 +744,10 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5, struct pt_regs *regs = RELOC_HIDE(&__regs, 0); unsigned long error_code; struct task_struct *tsk = current; + + if (kprobe_handle_illslot(regs->pc) == 0) + return; + #ifdef CONFIG_SH_FPU_EMU unsigned short inst = 0; -- cgit v1.2.3 From 037c10a612e8b7461e33672fb3848807ac6e2346 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 8 Sep 2008 12:22:47 +0900 Subject: sh: kprobes: Hook up kprobe_fault_handler() in the page fault path. Signed-off-by: Paul Mundt --- arch/sh/include/asm/kprobes.h | 3 +-- arch/sh/kernel/kprobes.c | 2 +- arch/sh/mm/fault_32.c | 29 ++++++++++++++++++++++++++++- 3 files changed, 30 insertions(+), 4 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/kprobes.h b/arch/sh/include/asm/kprobes.h index 70fc629df904..756a5cd96378 100644 --- a/arch/sh/include/asm/kprobes.h +++ b/arch/sh/include/asm/kprobes.h @@ -6,8 +6,6 @@ #include #include -struct pt_regs; - typedef u16 kprobe_opcode_t; #define BREAKPOINT_INSTRUCTION 0xc3ff @@ -48,6 +46,7 @@ struct kprobe_ctlblk { struct prev_kprobe prev_kprobe; }; +extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr); extern int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *data); extern int kprobe_handle_illslot(unsigned long pc); diff --git a/arch/sh/kernel/kprobes.c b/arch/sh/kernel/kprobes.c index ac6074942fd6..81a3725e5155 100644 --- a/arch/sh/kernel/kprobes.c +++ b/arch/sh/kernel/kprobes.c @@ -393,7 +393,7 @@ static inline int post_kprobe_handler(struct pt_regs *regs) return 1; } -static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) +int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) { struct kprobe *cur = kprobe_running(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c index e8efda9846bb..659811c179e6 100644 --- a/arch/sh/mm/fault_32.c +++ b/arch/sh/mm/fault_32.c @@ -2,7 +2,7 @@ * Page fault handler for SH with an MMU. * * Copyright (C) 1999 Niibe Yutaka - * Copyright (C) 2003 - 2007 Paul Mundt + * Copyright (C) 2003 - 2008 Paul Mundt * * Based on linux/arch/i386/mm/fault.c: * Copyright (C) 1995 Linus Torvalds @@ -21,6 +21,27 @@ #include #include +#ifdef CONFIG_KPROBES +static inline int notify_page_fault(struct pt_regs *regs, int trap) +{ + int ret = 0; + + if (!user_mode(regs)) { + preempt_disable(); + if (kprobe_running() && kprobe_fault_handler(regs, trap)) + ret = 1; + preempt_enable(); + } + + return ret; +} +#else +static inline int notify_page_fault(struct pt_regs *regs, int trap) +{ + return 0; +} +#endif + /* * This routine handles page faults. It determines the address, * and the problem, and then passes it off to one of the appropriate @@ -37,6 +58,9 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, int fault; siginfo_t info; + if (notify_page_fault(regs, writeaccess)) + return; + #ifdef CONFIG_SH_KGDB if (kgdb_nofault && kgdb_bus_err_hook) kgdb_bus_err_hook(); @@ -269,6 +293,9 @@ asmlinkage int __kprobes __do_page_fault(struct pt_regs *regs, pte_t *pte; pte_t entry; + if (notify_page_fault(regs, writeaccess)) + return 0; + #ifdef CONFIG_SH_KGDB if (kgdb_nofault && kgdb_bus_err_hook) kgdb_bus_err_hook(); -- cgit v1.2.3 From 174b5c9923e0170c844e03d55a9f3fb3b329a8b7 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 8 Sep 2008 18:10:10 +0900 Subject: sh: kprobes: Use trapa #0x3a for breakpoint trap. Not all parts support trapa #0xff, so use something within the debug trap range that's accessible on all parts. Signed-off-by: Paul Mundt --- arch/sh/include/asm/kprobes.h | 2 +- arch/sh/kernel/kprobes.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/kprobes.h b/arch/sh/include/asm/kprobes.h index 756a5cd96378..6078d8e551d4 100644 --- a/arch/sh/include/asm/kprobes.h +++ b/arch/sh/include/asm/kprobes.h @@ -7,7 +7,7 @@ #include typedef u16 kprobe_opcode_t; -#define BREAKPOINT_INSTRUCTION 0xc3ff +#define BREAKPOINT_INSTRUCTION 0xc33a #define MAX_INSN_SIZE 16 #define MAX_STACK_SIZE 64 diff --git a/arch/sh/kernel/kprobes.c b/arch/sh/kernel/kprobes.c index 81a3725e5155..fdd049e9ad86 100644 --- a/arch/sh/kernel/kprobes.c +++ b/arch/sh/kernel/kprobes.c @@ -525,8 +525,7 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) void __kprobes jprobe_return(void) { - __asm("trapa #-1\n\t" "jprobe_return_end:\n\t" "nop\n\t"); - + asm volatile ("trapa #0x3a\n\t" "jprobe_return_end:\n\t" "nop\n\t"); } int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) -- cgit v1.2.3 From 934135c19d8a1be435bae75aefc09b8ae1698b16 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 12 Sep 2008 19:52:36 +0900 Subject: sh: ptrace: Introduce user_regset interface for gp regs. Signed-off-by: Paul Mundt --- arch/sh/include/asm/elf.h | 9 ++++ arch/sh/include/asm/ptrace.h | 8 ++- arch/sh/kernel/ptrace_32.c | 116 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 115 insertions(+), 18 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/elf.h b/arch/sh/include/asm/elf.h index f01449a8d378..455d9e1e1438 100644 --- a/arch/sh/include/asm/elf.h +++ b/arch/sh/include/asm/elf.h @@ -108,6 +108,15 @@ typedef struct user_fpu_struct elf_fpregset_t; #define elf_check_fdpic(x) ((x)->e_flags & EF_SH_FDPIC) #define elf_check_const_displacement(x) ((x)->e_flags & EF_SH_PIC) +#if defined(CONFIG_SUPERH32) && \ + (!defined(CONFIG_SH_FPU) && !defined(CONFIG_SH_DSP)) +/* + * Enable dump using regset for general purpose registers, use this as + * the default once the FPU and DSP registers are moved over also. + */ +#define CORE_DUMP_USE_REGSET +#endif + #define USE_ELF_CORE_DUMP #define ELF_FDPIC_CORE_EFLAGS EF_SH_FDPIC #define ELF_EXEC_PAGESIZE PAGE_SIZE diff --git a/arch/sh/include/asm/ptrace.h b/arch/sh/include/asm/ptrace.h index b86aeabba61a..bf73646e2d2b 100644 --- a/arch/sh/include/asm/ptrace.h +++ b/arch/sh/include/asm/ptrace.h @@ -87,12 +87,18 @@ struct pt_dspregs { unsigned long mod; }; +#define PTRACE_GETREGS 12 /* General registers */ +#define PTRACE_SETREGS 13 + +#define PTRACE_GETFPREGS 14 /* FPU registers */ +#define PTRACE_SETFPREGS 15 + #define PTRACE_GETFDPIC 31 /* get the ELF fdpic loadmap address */ #define PTRACE_GETFDPIC_EXEC 0 /* [addr] request the executable loadmap */ #define PTRACE_GETFDPIC_INTERP 1 /* [addr] request the interpreter loadmap */ -#define PTRACE_GETDSPREGS 55 +#define PTRACE_GETDSPREGS 55 /* DSP registers */ #define PTRACE_SETDSPREGS 56 #endif diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 84bf3420597c..5e3ba10255cd 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -1,12 +1,14 @@ /* - * linux/arch/sh/kernel/ptrace.c + * SuperH process tracing * - * Original x86 implementation: - * By Ross Biro 1/23/92 - * edited by Linus Torvalds + * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka + * Copyright (C) 2002 - 2008 Paul Mundt * - * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka - * Audit support: Yuichi Nakamura + * Audit support by Yuichi Nakamura + * + * 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 @@ -22,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -29,11 +33,6 @@ #include #include -/* - * does not yet catch signals sent when the child dies. - * in exit.c or in signal.c. - */ - /* * This routine will get a word off of the process kernel stack. */ @@ -62,16 +61,12 @@ static inline int put_stack_long(struct task_struct *task, int offset, void user_enable_single_step(struct task_struct *child) { - struct pt_regs *regs = task_pt_regs(child); - long pc; - - pc = get_stack_long(child, (long)®s->pc); - /* Next scheduling will set up UBC */ if (child->thread.ubc_pc == 0) ubc_usercnt += 1; - child->thread.ubc_pc = pc; + child->thread.ubc_pc = get_stack_long(child, + offsetof(struct pt_regs, pc)); set_tsk_thread_flag(child, TIF_SINGLESTEP); } @@ -103,6 +98,83 @@ void ptrace_disable(struct task_struct *child) user_disable_single_step(child); } +static int genregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct pt_regs *regs = task_pt_regs(target); + int ret; + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + regs->regs, + 0, 16 * sizeof(unsigned long)); + if (!ret) + /* PC, PR, SR, GBR, MACH, MACL, TRA */ + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + ®s->pc, + offsetof(struct pt_regs, pc), + sizeof(struct pt_regs)); + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + sizeof(struct pt_regs), -1); + + return ret; +} + +static int genregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_pt_regs(target); + int ret; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + regs->regs, + 0, 16 * sizeof(unsigned long)); + if (!ret && count > 0) + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + ®s->pc, + offsetof(struct pt_regs, pc), + sizeof(struct pt_regs)); + if (!ret) + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + sizeof(struct pt_regs), -1); + + return ret; +} + +/* + * These are our native regset flavours. + */ +enum sh_regset { + REGSET_GENERAL, +}; + +static const struct user_regset sh_regsets[] = { + /* + * Format is: + * R0 --> R15 + * PC, PR, SR, GBR, MACH, MACL, TRA + */ + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = ELF_NGREG, + .size = sizeof(long), + .align = sizeof(long), + .get = genregs_get, + .set = genregs_set, + }, +}; + +static const struct user_regset_view user_sh_native_view = { + .name = "sh", + .e_machine = EM_SH, + .regsets = sh_regsets, + .n = ARRAY_SIZE(sh_regsets), +}; + long arch_ptrace(struct task_struct *child, long request, long addr, long data) { struct user * dummy = NULL; @@ -159,6 +231,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) } break; + case PTRACE_GETREGS: + return copy_regset_to_user(child, &user_sh_native_view, + REGSET_GENERAL, + 0, sizeof(struct pt_regs), + (void __user *)data); + case PTRACE_SETREGS: + return copy_regset_from_user(child, &user_sh_native_view, + REGSET_GENERAL, + 0, sizeof(struct pt_regs), + (const void __user *)data); #ifdef CONFIG_SH_DSP case PTRACE_GETDSPREGS: { unsigned long dp; -- cgit v1.2.3 From cb700aa4f13d38726defab3060d3ebeaf67dc189 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 12 Sep 2008 20:41:05 +0900 Subject: sh: ioremap_prot support. Signed-off-by: Paul Mundt --- arch/sh/Kconfig | 1 + arch/sh/include/asm/io.h | 2 ++ arch/sh/include/asm/page.h | 2 ++ arch/sh/include/asm/pgtable.h | 1 + 4 files changed, 6 insertions(+) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index bbdcd6418ef5..434183eb4f12 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -12,6 +12,7 @@ config SUPERH select HAVE_IDE select HAVE_OPROFILE select HAVE_GENERIC_DMA_COHERENT + select HAVE_IOREMAP_PROT help The SuperH is a RISC processor targeted for use in embedded systems and consumer electronics; it was also used in the Sega Dreamcast diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h index e49cfee95fb2..1857666136f2 100644 --- a/arch/sh/include/asm/io.h +++ b/arch/sh/include/asm/io.h @@ -347,6 +347,8 @@ __ioremap_mode(unsigned long offset, unsigned long size, unsigned long flags) __ioremap_mode((offset), (size), _PAGE_CACHABLE) #define p3_ioremap(offset, size, flags) \ __ioremap((offset), (size), (flags)) +#define ioremap_prot(offset, size, flags) \ + __ioremap_mode((offset), (size), (flags)) #define iounmap(addr) \ __iounmap((addr)) diff --git a/arch/sh/include/asm/page.h b/arch/sh/include/asm/page.h index 77fb8bf02e4e..5871d78e47e5 100644 --- a/arch/sh/include/asm/page.h +++ b/arch/sh/include/asm/page.h @@ -104,6 +104,8 @@ typedef struct { unsigned long pgd; } pgd_t; typedef struct page *pgtable_t; +#define pte_pgprot(x) __pgprot(pte_val(x) & PTE_FLAGS_MASK) + #endif /* !__ASSEMBLY__ */ /* diff --git a/arch/sh/include/asm/pgtable.h b/arch/sh/include/asm/pgtable.h index a4a8f8b93463..52220d70a096 100644 --- a/arch/sh/include/asm/pgtable.h +++ b/arch/sh/include/asm/pgtable.h @@ -76,6 +76,7 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; #endif #define PTE_PHYS_MASK (PHYS_ADDR_MASK & PAGE_MASK) +#define PTE_FLAGS_MASK (~(PTE_PHYS_MASK) << PAGE_SHIFT) #ifdef CONFIG_SUPERH32 #define VMALLOC_START (P3SEG) -- cgit v1.2.3 From 9996b42ac06adb7555933366e071ec8824bcaa37 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 12 Sep 2008 22:11:36 +0900 Subject: sh: provide user_stack_pointer(), needed for tracehook support. Signed-off-by: Paul Mundt --- arch/sh/include/asm/processor_32.h | 2 ++ arch/sh/include/asm/processor_64.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/processor_32.h b/arch/sh/include/asm/processor_32.h index 41d23210583c..1cd3a144c85c 100644 --- a/arch/sh/include/asm/processor_32.h +++ b/arch/sh/include/asm/processor_32.h @@ -199,6 +199,8 @@ extern unsigned long get_wchan(struct task_struct *p); #define KSTK_EIP(tsk) (task_pt_regs(tsk)->pc) #define KSTK_ESP(tsk) (task_pt_regs(tsk)->regs[15]) +#define user_stack_pointer(regs) ((regs)->regs[15]) + #define cpu_sleep() __asm__ __volatile__ ("sleep" : : : "memory") #define cpu_relax() barrier() diff --git a/arch/sh/include/asm/processor_64.h b/arch/sh/include/asm/processor_64.h index 16609bc11627..ae19839f1d97 100644 --- a/arch/sh/include/asm/processor_64.h +++ b/arch/sh/include/asm/processor_64.h @@ -267,6 +267,8 @@ extern unsigned long get_wchan(struct task_struct *p); #define KSTK_EIP(tsk) ((tsk)->thread.pc) #define KSTK_ESP(tsk) ((tsk)->thread.sp) +#define user_stack_pointer(regs) ((regs)->sp) + #define cpu_relax() barrier() #endif /* __ASSEMBLY__ */ -- cgit v1.2.3 From fb4f87a2f048b4cb1a499c9baa78f1d8437b09c3 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 12 Sep 2008 22:13:13 +0900 Subject: sh: Provide the asm/syscall.h interface, needed by tracehook. Signed-off-by: Paul Mundt --- arch/sh/include/asm/syscall.h | 10 ++++ arch/sh/include/asm/syscall_32.h | 110 +++++++++++++++++++++++++++++++++++++++ arch/sh/include/asm/syscall_64.h | 6 +++ 3 files changed, 126 insertions(+) create mode 100644 arch/sh/include/asm/syscall.h create mode 100644 arch/sh/include/asm/syscall_32.h create mode 100644 arch/sh/include/asm/syscall_64.h (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/syscall.h b/arch/sh/include/asm/syscall.h new file mode 100644 index 000000000000..6a381429ee9d --- /dev/null +++ b/arch/sh/include/asm/syscall.h @@ -0,0 +1,10 @@ +#ifndef __ASM_SH_SYSCALL_H +#define __ASM_SH_SYSCALL_H + +#ifdef CONFIG_SUPERH32 +# include "syscall_32.h" +#else +# include "syscall_64.h" +#endif + +#endif /* __ASM_SH_SYSCALL_H */ diff --git a/arch/sh/include/asm/syscall_32.h b/arch/sh/include/asm/syscall_32.h new file mode 100644 index 000000000000..54773f26cd44 --- /dev/null +++ b/arch/sh/include/asm/syscall_32.h @@ -0,0 +1,110 @@ +#ifndef __ASM_SH_SYSCALL_32_H +#define __ASM_SH_SYSCALL_32_H + +#include +#include +#include + +/* The system call number is given by the user in %g1 */ +static inline long syscall_get_nr(struct task_struct *task, + struct pt_regs *regs) +{ + return (regs->tra >= 0) ? regs->regs[3] : -1L; +} + +static inline void syscall_rollback(struct task_struct *task, + struct pt_regs *regs) +{ + /* + * XXX: This needs some thought. On SH we don't + * save away the original r0 value anywhere. + */ +} + +static inline bool syscall_has_error(struct pt_regs *regs) +{ + return (regs->sr & 0x1) ? true : false; +} +static inline void syscall_set_error(struct pt_regs *regs) +{ + regs->sr |= 0x1; +} +static inline void syscall_clear_error(struct pt_regs *regs) +{ + regs->sr &= ~0x1; +} + +static inline long syscall_get_error(struct task_struct *task, + struct pt_regs *regs) +{ + return syscall_has_error(regs) ? regs->regs[0] : 0; +} + +static inline long syscall_get_return_value(struct task_struct *task, + struct pt_regs *regs) +{ + return regs->regs[0]; +} + +static inline void syscall_set_return_value(struct task_struct *task, + struct pt_regs *regs, + int error, long val) +{ + if (error) { + syscall_set_error(regs); + regs->regs[0] = -error; + } else { + syscall_clear_error(regs); + regs->regs[0] = val; + } +} + +static inline void syscall_get_arguments(struct task_struct *task, + struct pt_regs *regs, + unsigned int i, unsigned int n, + unsigned long *args) +{ + /* + * Do this simply for now. If we need to start supporting + * fetching arguments from arbitrary indices, this will need some + * extra logic. Presently there are no in-tree users that depend + * on this behaviour. + */ + BUG_ON(i); + + /* Argument pattern is: R4, R5, R6, R7, R0, R1 */ + switch (n) { + case 6: args[5] = regs->regs[1]; + case 5: args[4] = regs->regs[0]; + case 4: args[3] = regs->regs[7]; + case 3: args[2] = regs->regs[6]; + case 2: args[1] = regs->regs[5]; + case 1: args[0] = regs->regs[4]; + break; + default: + BUG(); + } +} + +static inline void syscall_set_arguments(struct task_struct *task, + struct pt_regs *regs, + unsigned int i, unsigned int n, + const unsigned long *args) +{ + /* Same note as above applies */ + BUG_ON(i); + + switch (n) { + case 6: regs->regs[1] = args[5]; + case 5: regs->regs[0] = args[4]; + case 4: regs->regs[7] = args[3]; + case 3: regs->regs[6] = args[2]; + case 2: regs->regs[5] = args[1]; + case 1: regs->regs[4] = args[0]; + break; + default: + BUG(); + } +} + +#endif /* __ASM_SH_SYSCALL_32_H */ diff --git a/arch/sh/include/asm/syscall_64.h b/arch/sh/include/asm/syscall_64.h new file mode 100644 index 000000000000..bcaaa8ca4d70 --- /dev/null +++ b/arch/sh/include/asm/syscall_64.h @@ -0,0 +1,6 @@ +#ifndef __ASM_SH_SYSCALL_64_H +#define __ASM_SH_SYSCALL_64_H + +#include + +#endif /* __ASM_SH_SYSCALL_64_H */ -- cgit v1.2.3 From 6bff1592d85c9fa1f1d9d4de1cd0e104279544a6 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 12 Sep 2008 22:41:30 +0900 Subject: sh: Fix up NUMA build error with se7722_defconfig. Signed-off-by: Paul Mundt --- arch/sh/include/asm/mmzone.h | 2 ++ arch/sh/include/asm/setup.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/mmzone.h b/arch/sh/include/asm/mmzone.h index 2969253c4042..7f5363b29ba0 100644 --- a/arch/sh/include/asm/mmzone.h +++ b/arch/sh/include/asm/mmzone.h @@ -4,6 +4,8 @@ #ifdef __KERNEL__ #ifdef CONFIG_NEED_MULTIPLE_NODES +#include + extern struct pglist_data *node_data[]; #define NODE_DATA(nid) (node_data[nid]) diff --git a/arch/sh/include/asm/setup.h b/arch/sh/include/asm/setup.h index 554f865075ca..1b7856f5924c 100644 --- a/arch/sh/include/asm/setup.h +++ b/arch/sh/include/asm/setup.h @@ -1,7 +1,7 @@ #ifndef _SH_SETUP_H #define _SH_SETUP_H -#include +#include #define COMMAND_LINE_SIZE 256 -- cgit v1.2.3 From 5dadb34394d59313e2e763ae8e2fc911e9fc557c Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 12 Sep 2008 22:42:10 +0900 Subject: sh: Add DSP registers to regset interface. Signed-off-by: Paul Mundt --- arch/sh/Kconfig | 2 +- arch/sh/include/asm/elf.h | 5 ++- arch/sh/include/asm/ptrace.h | 3 ++ arch/sh/kernel/ptrace_32.c | 86 ++++++++++++++++++++++++++++++-------------- 4 files changed, 65 insertions(+), 31 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index f995d134ab3a..acaba1bdd8a6 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -23,7 +23,7 @@ config SUPERH32 def_bool !SUPERH64 select HAVE_KPROBES select HAVE_KRETPROBES - select HAVE_ARCH_TRACEHOOK if (!SH_FPU && !SH_DSP) + select HAVE_ARCH_TRACEHOOK if !SH_FPU config SUPERH64 def_bool y if CPU_SH5 diff --git a/arch/sh/include/asm/elf.h b/arch/sh/include/asm/elf.h index 455d9e1e1438..7c2363f8250e 100644 --- a/arch/sh/include/asm/elf.h +++ b/arch/sh/include/asm/elf.h @@ -108,11 +108,10 @@ typedef struct user_fpu_struct elf_fpregset_t; #define elf_check_fdpic(x) ((x)->e_flags & EF_SH_FDPIC) #define elf_check_const_displacement(x) ((x)->e_flags & EF_SH_PIC) -#if defined(CONFIG_SUPERH32) && \ - (!defined(CONFIG_SH_FPU) && !defined(CONFIG_SH_DSP)) +#if defined(CONFIG_SUPERH32) && !defined(CONFIG_SH_FPU) /* * Enable dump using regset for general purpose registers, use this as - * the default once the FPU and DSP registers are moved over also. + * the default once the FPU registers are moved over also. */ #define CORE_DUMP_USE_REGSET #endif diff --git a/arch/sh/include/asm/ptrace.h b/arch/sh/include/asm/ptrace.h index bf73646e2d2b..3ad18e91bca6 100644 --- a/arch/sh/include/asm/ptrace.h +++ b/arch/sh/include/asm/ptrace.h @@ -123,6 +123,9 @@ extern void user_disable_single_step(struct task_struct *); #define task_pt_regs(task) \ ((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE \ - sizeof(struct pt_dspregs) - sizeof(unsigned long)) - 1) +#define task_pt_dspregs(task) \ + ((struct pt_dspregs *) (task_stack_page(task) + THREAD_SIZE \ + - sizeof(unsigned long)) - 1) #else #define task_pt_regs(task) \ ((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE \ diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 5e3ba10255cd..20b103f6c33b 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -145,11 +145,50 @@ static int genregs_set(struct task_struct *target, return ret; } +#ifdef CONFIG_SH_DSP +static int dspregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct pt_dspregs *regs = task_pt_dspregs(target); + int ret; + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs, + 0, sizeof(struct pt_dspregs)); + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + sizeof(struct pt_dspregs), -1); + + return ret; +} + +static int dspregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_dspregs *regs = task_pt_dspregs(target); + int ret; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, regs, + 0, sizeof(struct pt_dspregs)); + if (!ret) + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + sizeof(struct pt_dspregs), -1); + + return ret; +} +#endif + /* * These are our native regset flavours. */ enum sh_regset { REGSET_GENERAL, +#ifdef CONFIG_SH_DSP + REGSET_DSP, +#endif }; static const struct user_regset sh_regsets[] = { @@ -166,6 +205,16 @@ static const struct user_regset sh_regsets[] = { .get = genregs_get, .set = genregs_set, }, + +#ifdef CONFIG_SH_DSP + [REGSET_DSP] = { + .n = sizeof(struct pt_dspregs) / sizeof(long), + .size = sizeof(long), + .align = sizeof(long), + .get = dspregs_get, + .set = dspregs_set, + }, +#endif }; static const struct user_regset_view user_sh_native_view = { @@ -242,33 +291,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) 0, sizeof(struct pt_regs), (const void __user *)data); #ifdef CONFIG_SH_DSP - case PTRACE_GETDSPREGS: { - unsigned long dp; - - ret = -EIO; - dp = ((unsigned long) child) + THREAD_SIZE - - sizeof(struct pt_dspregs); - if (*((int *) (dp - 4)) == SR_FD) { - copy_to_user((void *)addr, (void *) dp, - sizeof(struct pt_dspregs)); - ret = 0; - } - break; - } - - case PTRACE_SETDSPREGS: { - unsigned long dp; - - ret = -EIO; - dp = ((unsigned long) child) + THREAD_SIZE - - sizeof(struct pt_dspregs); - if (*((int *) (dp - 4)) == SR_FD) { - copy_from_user((void *) dp, (void *)addr, - sizeof(struct pt_dspregs)); - ret = 0; - } - break; - } + case PTRACE_GETDSPREGS: + return copy_regset_to_user(child, &user_sh_native_view, + REGSET_DSP, + 0, sizeof(struct pt_dspregs), + (void __user *)data); + case PTRACE_SETDSPREGS: + return copy_regset_from_user(child, &user_sh_native_view, + REGSET_DSP, + 0, sizeof(struct pt_dspregs), + (const void __user *)data); #endif #ifdef CONFIG_BINFMT_ELF_FDPIC case PTRACE_GETFDPIC: { -- cgit v1.2.3 From 0e660d2d433393f983cd58fe8c54f831fa7c7713 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 12 Sep 2008 23:27:46 +0900 Subject: sh: Tidy up ELF core dumps. These have been using overrides for ELF_CORE_COPY_TASK_REGS and ELF_CORE_COPY_FPREGS while the generic versions can be used instead. Presently the pt_regs are also duplicated across elf_core_copy_regs() and elf_core_copy_task_regs(), this switches to simply copying out through elf_core_copy_regs() instead. Signed-off-by: Paul Mundt --- arch/sh/include/asm/elf.h | 6 ------ arch/sh/kernel/Makefile_32 | 1 - arch/sh/kernel/Makefile_64 | 1 - arch/sh/kernel/dump_task.c | 32 -------------------------------- 4 files changed, 40 deletions(-) delete mode 100644 arch/sh/kernel/dump_task.c (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/elf.h b/arch/sh/include/asm/elf.h index 7c2363f8250e..6b2cec80fd15 100644 --- a/arch/sh/include/asm/elf.h +++ b/arch/sh/include/asm/elf.h @@ -198,12 +198,6 @@ do { \ #endif #define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX_32BIT) -struct task_struct; -extern int dump_task_regs (struct task_struct *, elf_gregset_t *); -extern int dump_task_fpu (struct task_struct *, elf_fpregset_t *); - -#define ELF_CORE_COPY_TASK_REGS(tsk, elf_regs) dump_task_regs(tsk, elf_regs) -#define ELF_CORE_COPY_FPREGS(tsk, elf_fpregs) dump_task_fpu(tsk, elf_fpregs) #ifdef CONFIG_VSYSCALL /* vDSO has arch_setup_additional_pages */ diff --git a/arch/sh/kernel/Makefile_32 b/arch/sh/kernel/Makefile_32 index 12e617beba20..8497de8afbff 100644 --- a/arch/sh/kernel/Makefile_32 +++ b/arch/sh/kernel/Makefile_32 @@ -21,7 +21,6 @@ obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_STACKTRACE) += stacktrace.o -obj-$(CONFIG_ELF_CORE) += dump_task.o obj-$(CONFIG_IO_TRAPPED) += io_trapped.o obj-$(CONFIG_KPROBES) += kprobes.o diff --git a/arch/sh/kernel/Makefile_64 b/arch/sh/kernel/Makefile_64 index 6edf53b93d94..e987eb2e9bb5 100644 --- a/arch/sh/kernel/Makefile_64 +++ b/arch/sh/kernel/Makefile_64 @@ -17,7 +17,6 @@ obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_STACKTRACE) += stacktrace.o -obj-$(CONFIG_BINFMT_ELF) += dump_task.o obj-$(CONFIG_IO_TRAPPED) += io_trapped.o EXTRA_CFLAGS += -Werror diff --git a/arch/sh/kernel/dump_task.c b/arch/sh/kernel/dump_task.c deleted file mode 100644 index 1db7ce0f25d4..000000000000 --- a/arch/sh/kernel/dump_task.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include -#include - -/* - * Capture the user space registers if the task is not running (in user space) - */ -int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) -{ - struct pt_regs ptregs; - - ptregs = *task_pt_regs(tsk); - elf_core_copy_regs(regs, &ptregs); - - return 1; -} - -int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpu) -{ - int fpvalid = 0; - -#if defined(CONFIG_SH_FPU) - fpvalid = !!tsk_used_math(tsk); - if (fpvalid) { - unlazy_fpu(tsk, task_pt_regs(tsk)); - memcpy(fpu, &tsk->thread.fpu.hard, sizeof(*fpu)); - } -#endif - - return fpvalid; -} - -- cgit v1.2.3 From 81b669952ed5fe0d6f65f8b9a97d1fdeac93ff10 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 17 Sep 2008 23:24:02 +0900 Subject: sh: Consolidate struct sh_cpuinfo definitions across _32/_64 split. Signed-off-by: Paul Mundt --- arch/sh/include/asm/processor.h | 40 ++++++++++++++++++++++++++++++++++++- arch/sh/include/asm/processor_32.h | 19 ------------------ arch/sh/include/asm/processor_64.h | 41 -------------------------------------- 3 files changed, 39 insertions(+), 61 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/processor.h b/arch/sh/include/asm/processor.h index 58e2be55ab93..693364a20ad7 100644 --- a/arch/sh/include/asm/processor.h +++ b/arch/sh/include/asm/processor.h @@ -3,6 +3,7 @@ #include #include +#include #ifndef __ASSEMBLY__ /* @@ -43,8 +44,45 @@ enum cpu_type { CPU_SH_NONE }; +/* + * TLB information structure + * + * Defined for both I and D tlb, per-processor. + */ +struct tlb_info { + unsigned long long next; + unsigned long long first; + unsigned long long last; + + unsigned int entries; + unsigned int step; + + unsigned long flags; +}; + +struct sh_cpuinfo { + unsigned int type; + int cut_major, cut_minor; + unsigned long loops_per_jiffy; + unsigned long asid_cache; + + struct cache_info icache; /* Primary I-cache */ + struct cache_info dcache; /* Primary D-cache */ + struct cache_info scache; /* Secondary cache */ + + /* TLB info */ + struct tlb_info itlb; + struct tlb_info dtlb; + + unsigned long flags; +} __attribute__ ((aligned(L1_CACHE_BYTES))); + +extern struct sh_cpuinfo cpu_data[]; +#define boot_cpu_data cpu_data[0] +#define current_cpu_data cpu_data[smp_processor_id()] +#define raw_current_cpu_data cpu_data[raw_smp_processor_id()] + /* Forward decl */ -struct sh_cpuinfo; struct seq_operations; extern struct pt_regs fake_swapper_regs; diff --git a/arch/sh/include/asm/processor_32.h b/arch/sh/include/asm/processor_32.h index 1cd3a144c85c..a46a0207e977 100644 --- a/arch/sh/include/asm/processor_32.h +++ b/arch/sh/include/asm/processor_32.h @@ -13,7 +13,6 @@ #include #include #include -#include #include /* @@ -27,24 +26,6 @@ #define CCN_CVR 0xff000040 #define CCN_PRR 0xff000044 -struct sh_cpuinfo { - unsigned int type; - int cut_major, cut_minor; - unsigned long loops_per_jiffy; - unsigned long asid_cache; - - struct cache_info icache; /* Primary I-cache */ - struct cache_info dcache; /* Primary D-cache */ - struct cache_info scache; /* Secondary cache */ - - unsigned long flags; -} __attribute__ ((aligned(L1_CACHE_BYTES))); - -extern struct sh_cpuinfo cpu_data[]; -#define boot_cpu_data cpu_data[0] -#define current_cpu_data cpu_data[smp_processor_id()] -#define raw_current_cpu_data cpu_data[raw_smp_processor_id()] - asmlinkage void __init sh_cpu_init(void); /* diff --git a/arch/sh/include/asm/processor_64.h b/arch/sh/include/asm/processor_64.h index ae19839f1d97..b0b4824dfc4c 100644 --- a/arch/sh/include/asm/processor_64.h +++ b/arch/sh/include/asm/processor_64.h @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -36,46 +35,6 @@ __asm__("gettr tr0, %1\n\t" \ : "1" (__dummy)); \ pc; }) -/* - * TLB information structure - * - * Defined for both I and D tlb, per-processor. - */ -struct tlb_info { - unsigned long long next; - unsigned long long first; - unsigned long long last; - - unsigned int entries; - unsigned int step; - - unsigned long flags; -}; - -struct sh_cpuinfo { - enum cpu_type type; - unsigned long loops_per_jiffy; - unsigned long asid_cache; - - unsigned int cpu_clock, master_clock, bus_clock, module_clock; - - /* Cache info */ - struct cache_info icache; - struct cache_info dcache; - struct cache_info scache; - - /* TLB info */ - struct tlb_info itlb; - struct tlb_info dtlb; - - unsigned long flags; -}; - -extern struct sh_cpuinfo cpu_data[]; -#define boot_cpu_data cpu_data[0] -#define current_cpu_data cpu_data[smp_processor_id()] -#define raw_current_cpu_data cpu_data[raw_smp_processor_id()] - #endif /* -- cgit v1.2.3 From b406efefd5246a67c691fd79871e65ce8f3f91ff Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 17 Sep 2008 23:24:59 +0900 Subject: sh: Fix up headers_check regression. linux/mmzone.h isn't exported, kill it off from asm/setup.h and simply deal with it in the places that have a dependency instead. Signed-off-by: Paul Mundt --- arch/sh/include/asm/setup.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/setup.h b/arch/sh/include/asm/setup.h index 1b7856f5924c..d450bcf59ee2 100644 --- a/arch/sh/include/asm/setup.h +++ b/arch/sh/include/asm/setup.h @@ -1,12 +1,9 @@ #ifndef _SH_SETUP_H #define _SH_SETUP_H -#include - #define COMMAND_LINE_SIZE 256 #ifdef __KERNEL__ - /* * This is set up by the setup-routine at boot-time */ -- cgit v1.2.3 From c15c5f8c2bf0b00d036c5c6b67264764a6e5dffc Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sat, 20 Sep 2008 20:21:33 +0900 Subject: sh: Support kernel stacks smaller than a page. This follows the powerpc commit f6a616800e68b61807d0f7bb0d5dc70665ef8046 '[POWERPC] Fix kernel stack allocation alignment'. SH has traditionally forced the thread order to be relative to the page size, so there were never any situations where the same bug was triggered by slub. Regardless, the usage of > 8kB stacks for the larger page sizes is overkill, so we switch to using slab allocations there, as per the powerpc change. Signed-off-by: Paul Mundt --- arch/sh/include/asm/thread_info.h | 32 ++++++++++++++------------------ arch/sh/mm/init.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 18 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/thread_info.h b/arch/sh/include/asm/thread_info.h index 0a894cafb1dd..f09ac4806294 100644 --- a/arch/sh/include/asm/thread_info.h +++ b/arch/sh/include/asm/thread_info.h @@ -33,20 +33,12 @@ struct thread_info { #define PREEMPT_ACTIVE 0x10000000 #if defined(CONFIG_4KSTACKS) -#define THREAD_SIZE_ORDER (0) -#elif defined(CONFIG_PAGE_SIZE_4KB) -#define THREAD_SIZE_ORDER (1) -#elif defined(CONFIG_PAGE_SIZE_8KB) -#define THREAD_SIZE_ORDER (1) -#elif defined(CONFIG_PAGE_SIZE_16KB) -#define THREAD_SIZE_ORDER (0) -#elif defined(CONFIG_PAGE_SIZE_64KB) -#define THREAD_SIZE_ORDER (0) +#define THREAD_SHIFT 12 #else -#error "Unknown thread size" +#define THREAD_SHIFT 13 #endif -#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) +#define THREAD_SIZE (1 << THREAD_SHIFT) #define STACK_WARN (THREAD_SIZE >> 3) /* @@ -94,15 +86,19 @@ static inline struct thread_info *current_thread_info(void) return ti; } +/* thread information allocation */ +#if THREAD_SHIFT >= PAGE_SHIFT + +#define THREAD_SIZE_ORDER (THREAD_SHIFT - PAGE_SHIFT) + +#else /* THREAD_SHIFT < PAGE_SHIFT */ + #define __HAVE_ARCH_THREAD_INFO_ALLOCATOR -/* thread information allocation */ -#ifdef CONFIG_DEBUG_STACK_USAGE -#define alloc_thread_info(ti) kzalloc(THREAD_SIZE, GFP_KERNEL) -#else -#define alloc_thread_info(ti) kmalloc(THREAD_SIZE, GFP_KERNEL) -#endif -#define free_thread_info(ti) kfree(ti) +extern struct thread_info *alloc_thread_info(struct task_struct *tsk); +extern void free_thread_info(struct thread_info *ti); + +#endif /* THREAD_SHIFT < PAGE_SHIFT */ #endif /* __ASSEMBLY__ */ diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 31211bfdc6d8..2a53943924b2 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -265,6 +265,35 @@ void free_initrd_mem(unsigned long start, unsigned long end) } #endif +#if THREAD_SHIFT < PAGE_SHIFT +static struct kmem_cache *thread_info_cache; + +struct thread_info *alloc_thread_info(struct task_struct *tsk) +{ + struct thread_info *ti; + + ti = kmem_cache_alloc(thread_info_cache, GFP_KERNEL); + if (unlikely(ti == NULL)) + return NULL; +#ifdef CONFIG_DEBUG_STACK_USAGE + memset(ti, 0, THREAD_SIZE); +#endif + return ti; +} + +void free_thread_info(struct thread_info *ti) +{ + kmem_cache_free(thread_info_cache, ti); +} + +void thread_info_cache_init(void) +{ + thread_info_cache = kmem_cache_create("thread_info", THREAD_SIZE, + THREAD_SIZE, 0, NULL); + BUG_ON(thread_info_cache == NULL); +} +#endif /* THREAD_SHIFT < PAGE_SHIFT */ + #ifdef CONFIG_MEMORY_HOTPLUG int arch_add_memory(int nid, u64 start, u64 size) { -- cgit v1.2.3 From 837c946aad480d4773619707f115ed4c15738f77 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sun, 21 Sep 2008 10:24:18 +0900 Subject: sh: Copy in asm/sizes.h helper from ARM. Signed-off-by: Paul Mundt --- arch/sh/include/asm/sizes.h | 56 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 arch/sh/include/asm/sizes.h (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/sizes.h b/arch/sh/include/asm/sizes.h new file mode 100644 index 000000000000..503843db1565 --- /dev/null +++ b/arch/sh/include/asm/sizes.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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 + */ +/* DO NOT EDIT!! - this file automatically generated + * from .s file by awk -f s2h.awk + */ +/* Size definitions + * Copyright (C) ARM Limited 1998. All rights reserved. + */ + +#ifndef __sizes_h +#define __sizes_h 1 + +/* handy sizes */ +#define SZ_16 0x00000010 +#define SZ_256 0x00000100 +#define SZ_512 0x00000200 + +#define SZ_1K 0x00000400 +#define SZ_4K 0x00001000 +#define SZ_8K 0x00002000 +#define SZ_16K 0x00004000 +#define SZ_64K 0x00010000 +#define SZ_128K 0x00020000 +#define SZ_256K 0x00040000 +#define SZ_512K 0x00080000 + +#define SZ_1M 0x00100000 +#define SZ_2M 0x00200000 +#define SZ_4M 0x00400000 +#define SZ_8M 0x00800000 +#define SZ_16M 0x01000000 +#define SZ_32M 0x02000000 +#define SZ_64M 0x04000000 +#define SZ_128M 0x08000000 +#define SZ_256M 0x10000000 +#define SZ_512M 0x20000000 + +#define SZ_1G 0x40000000 +#define SZ_2G 0x80000000 + +#endif + +/* END */ -- cgit v1.2.3 From d3ea00a36da2e715217f0e31944dd220deefa38c Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sun, 21 Sep 2008 10:31:57 +0900 Subject: sh: Add a few more definitions to asm/sizes.h. Signed-off-by: Paul Mundt --- arch/sh/include/asm/sizes.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/sizes.h b/arch/sh/include/asm/sizes.h index 503843db1565..3a1fb97770f1 100644 --- a/arch/sh/include/asm/sizes.h +++ b/arch/sh/include/asm/sizes.h @@ -25,6 +25,9 @@ /* handy sizes */ #define SZ_16 0x00000010 +#define SZ_32 0x00000020 +#define SZ_64 0x00000040 +#define SZ_128 0x00000080 #define SZ_256 0x00000100 #define SZ_512 0x00000200 @@ -32,6 +35,7 @@ #define SZ_4K 0x00001000 #define SZ_8K 0x00002000 #define SZ_16K 0x00004000 +#define SZ_32K 0x00008000 #define SZ_64K 0x00010000 #define SZ_128K 0x00020000 #define SZ_256K 0x00040000 @@ -42,6 +46,7 @@ #define SZ_4M 0x00400000 #define SZ_8M 0x00800000 #define SZ_16M 0x01000000 +#define SZ_26M 0x01a00000 #define SZ_32M 0x02000000 #define SZ_64M 0x04000000 #define SZ_128M 0x08000000 -- cgit v1.2.3 From 4c59e2942e92d2d776bcd038604a5c3c1d56d3ac Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sun, 21 Sep 2008 12:00:23 +0900 Subject: sh: Move lookup_exception_vector() out to asm/system_32.h. There are other places where we want to have access to the trap/exception number, so move out the lookup_exception_vector() helper. While we're at it, refactor it slightly to return the vector instead. Signed-off-by: Paul Mundt --- arch/sh/include/asm/system_32.h | 25 +++++++++++++++++++++++++ arch/sh/kernel/traps_32.c | 16 ++++------------ 2 files changed, 29 insertions(+), 12 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/system_32.h b/arch/sh/include/asm/system_32.h index f7f105627fd9..a726d5d07277 100644 --- a/arch/sh/include/asm/system_32.h +++ b/arch/sh/include/asm/system_32.h @@ -97,6 +97,31 @@ do { \ : "=&r" (__dummy)); \ } while (0) +#ifdef CONFIG_CPU_HAS_SR_RB +#define lookup_exception_vector() \ +({ \ + unsigned long _vec; \ + \ + __asm__ __volatile__ ( \ + "stc r2_bank, %0\n\t" \ + : "=r" (_vec) \ + ); \ + \ + _vec; \ +}) +#else +#define lookup_exception_vector() \ +({ \ + unsigned long _vec; \ + __asm__ __volatile__ ( \ + "mov r4, %0\n\t" \ + : "=r" (_vec) \ + ); \ + \ + _vec; \ +}) +#endif + int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs, struct mem_access *ma); diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 35b901ed6de3..b359b08a8e33 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -514,14 +514,6 @@ int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs, return ret; } -#ifdef CONFIG_CPU_HAS_SR_RB -#define lookup_exception_vector(x) \ - __asm__ __volatile__ ("stc r2_bank, %0\n\t" : "=r" ((x))) -#else -#define lookup_exception_vector(x) \ - __asm__ __volatile__ ("mov r4, %0\n\t" : "=r" ((x))) -#endif - /* * Handle various address error exceptions: * - instruction address error: @@ -545,7 +537,7 @@ asmlinkage void do_address_error(struct pt_regs *regs, /* Intentional ifdef */ #ifdef CONFIG_CPU_HAS_SR_RB - lookup_exception_vector(error_code); + error_code = lookup_exception_vector(); #endif oldfs = get_fs(); @@ -686,7 +678,7 @@ asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5, } #endif - lookup_exception_vector(error_code); + error_code = lookup_exception_vector(); local_irq_enable(); CHK_REMOTE_DEBUG(regs); @@ -759,7 +751,7 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5, /* not a FPU inst. */ #endif - lookup_exception_vector(inst); + inst = lookup_exception_vector(); local_irq_enable(); CHK_REMOTE_DEBUG(regs); @@ -774,7 +766,7 @@ asmlinkage void do_exception_error(unsigned long r4, unsigned long r5, struct pt_regs *regs = RELOC_HIDE(&__regs, 0); long ex; - lookup_exception_vector(ex); + ex = lookup_exception_vector(); die_if_kernel("exception", regs, ex); } -- cgit v1.2.3 From 4b4cf7595a8bce9b4dd64c241a8cb7336ecb9489 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sun, 21 Sep 2008 17:17:53 +0900 Subject: sh: Add missing asm/ftrace.h. This was missed with the ftrace support commit.. check it in now. Signed-off-by: Paul Mundt --- arch/sh/include/asm/ftrace.h | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 arch/sh/include/asm/ftrace.h (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/ftrace.h b/arch/sh/include/asm/ftrace.h new file mode 100644 index 000000000000..3aed362c9463 --- /dev/null +++ b/arch/sh/include/asm/ftrace.h @@ -0,0 +1,8 @@ +#ifndef __ASM_SH_FTRACE_H +#define __ASM_SH_FTRACE_H + +#ifndef __ASSEMBLY__ +extern void mcount(void); +#endif + +#endif /* __ASM_SH_FTRACE_H */ -- cgit v1.2.3 From e7ab3cd251926d57ee11d7d320e8fb42c882ad22 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sun, 21 Sep 2008 19:04:55 +0900 Subject: sh: Add FPU registers to regset interface. Signed-off-by: Paul Mundt --- arch/sh/Kconfig | 2 +- arch/sh/include/asm/elf.h | 6 ++-- arch/sh/include/asm/fpu.h | 19 ++++++++++++ arch/sh/kernel/process_32.c | 14 ++++++--- arch/sh/kernel/ptrace_32.c | 76 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 9 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 38a5a9edb677..71be7ff5c771 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -23,7 +23,7 @@ config SUPERH32 def_bool !SUPERH64 select HAVE_KPROBES select HAVE_KRETPROBES - select HAVE_ARCH_TRACEHOOK if !SH_FPU + select HAVE_ARCH_TRACEHOOK select HAVE_FTRACE config SUPERH64 diff --git a/arch/sh/include/asm/elf.h b/arch/sh/include/asm/elf.h index 6b2cec80fd15..4da3a0b10911 100644 --- a/arch/sh/include/asm/elf.h +++ b/arch/sh/include/asm/elf.h @@ -108,10 +108,10 @@ typedef struct user_fpu_struct elf_fpregset_t; #define elf_check_fdpic(x) ((x)->e_flags & EF_SH_FDPIC) #define elf_check_const_displacement(x) ((x)->e_flags & EF_SH_PIC) -#if defined(CONFIG_SUPERH32) && !defined(CONFIG_SH_FPU) +#ifdef CONFIG_SUPERH32 /* - * Enable dump using regset for general purpose registers, use this as - * the default once the FPU registers are moved over also. + * Enable dump using regset. + * This covers all of general/DSP/FPU regs. */ #define CORE_DUMP_USE_REGSET #endif diff --git a/arch/sh/include/asm/fpu.h b/arch/sh/include/asm/fpu.h index 91462fea1507..1d3aee04b5cc 100644 --- a/arch/sh/include/asm/fpu.h +++ b/arch/sh/include/asm/fpu.h @@ -30,8 +30,15 @@ static inline void save_fpu(struct task_struct *tsk, struct pt_regs *regs) } #endif +struct user_regset; + extern int do_fpu_inst(unsigned short, struct pt_regs *); +extern int fpregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf); + static inline void unlazy_fpu(struct task_struct *tsk, struct pt_regs *regs) { preempt_disable(); @@ -50,6 +57,18 @@ static inline void clear_fpu(struct task_struct *tsk, struct pt_regs *regs) preempt_enable(); } +static inline int init_fpu(struct task_struct *tsk) +{ + if (tsk_used_math(tsk)) { + if ((boot_cpu_data.flags & CPU_HAS_FPU) && tsk == current) + unlazy_fpu(tsk, task_pt_regs(tsk)); + return 0; + } + + set_stopped_child_used_math(tsk); + return 0; +} + #endif /* __ASSEMBLY__ */ #endif /* __ASM_SH_FPU_H */ diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c index 7b013aa8c43f..b965f0282c7d 100644 --- a/arch/sh/kernel/process_32.c +++ b/arch/sh/kernel/process_32.c @@ -7,7 +7,11 @@ * * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima * Copyright (C) 2006 Lineo Solutions Inc. support SH4A UBC - * Copyright (C) 2002 - 2007 Paul Mundt + * Copyright (C) 2002 - 2008 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 @@ -222,10 +226,10 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) struct task_struct *tsk = current; fpvalid = !!tsk_used_math(tsk); - if (fpvalid) { - unlazy_fpu(tsk, regs); - memcpy(fpu, &tsk->thread.fpu.hard, sizeof(*fpu)); - } + if (fpvalid) + fpvalid = !fpregs_get(tsk, NULL, 0, + sizeof(struct user_fpu_struct), + fpu, NULL); #endif return fpvalid; diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 0f44f2b51a60..29ca09d24ef8 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -32,6 +32,7 @@ #include #include #include +#include /* * This routine will get a word off of the process kernel stack. @@ -145,6 +146,54 @@ static int genregs_set(struct task_struct *target, return ret; } +#ifdef CONFIG_SH_FPU +int fpregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; + + ret = init_fpu(target); + if (ret) + return ret; + + if ((boot_cpu_data.flags & CPU_HAS_FPU)) + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.hard, 0, -1); + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.soft, 0, -1); +} + +static int fpregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret; + + ret = init_fpu(target); + if (ret) + return ret; + + set_stopped_child_used_math(target); + + if ((boot_cpu_data.flags & CPU_HAS_FPU)) + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.hard, 0, -1); + + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.soft, 0, -1); +} + +static int fpregs_active(struct task_struct *target, + const struct user_regset *regset) +{ + return tsk_used_math(target) ? regset->n : 0; +} +#endif + #ifdef CONFIG_SH_DSP static int dspregs_get(struct task_struct *target, const struct user_regset *regset, @@ -194,6 +243,9 @@ static int dspregs_active(struct task_struct *target, */ enum sh_regset { REGSET_GENERAL, +#ifdef CONFIG_SH_FPU + REGSET_FPU, +#endif #ifdef CONFIG_SH_DSP REGSET_DSP, #endif @@ -214,6 +266,18 @@ static const struct user_regset sh_regsets[] = { .set = genregs_set, }, +#ifdef CONFIG_SH_FPU + [REGSET_FPU] = { + .core_note_type = NT_PRFPREG, + .n = sizeof(struct user_fpu_struct) / sizeof(long), + .size = sizeof(long), + .align = sizeof(long), + .get = fpregs_get, + .set = fpregs_set, + .active = fpregs_active, + }, +#endif + #ifdef CONFIG_SH_DSP [REGSET_DSP] = { .n = sizeof(struct pt_dspregs) / sizeof(long), @@ -304,6 +368,18 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) REGSET_GENERAL, 0, sizeof(struct pt_regs), (const void __user *)data); +#ifdef CONFIG_SH_FPU + case PTRACE_GETFPREGS: + return copy_regset_to_user(child, &user_sh_native_view, + REGSET_FPU, + 0, sizeof(struct user_fpu_struct), + (void __user *)data); + case PTRACE_SETFPREGS: + return copy_regset_from_user(child, &user_sh_native_view, + REGSET_FPU, + 0, sizeof(struct user_fpu_struct), + (const void __user *)data); +#endif #ifdef CONFIG_SH_DSP case PTRACE_GETDSPREGS: return copy_regset_to_user(child, &user_sh_native_view, -- cgit v1.2.3 From 88856d67cf6b787447889915bafd541be20b8630 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 29 Sep 2008 19:43:44 +0900 Subject: sh: Fix up uaccess_64 put/get_user() cast warnings. Signed-off-by: Paul Mundt --- arch/sh/include/asm/uaccess_64.h | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/uaccess_64.h b/arch/sh/include/asm/uaccess_64.h index 5580fd471003..0042c9054b20 100644 --- a/arch/sh/include/asm/uaccess_64.h +++ b/arch/sh/include/asm/uaccess_64.h @@ -26,16 +26,20 @@ do { \ retval = 0; \ switch (size) { \ case 1: \ - retval = __get_user_asm_b(x, ptr); \ + retval = __get_user_asm_b((void *)&x, \ + (long)ptr); \ break; \ case 2: \ - retval = __get_user_asm_w(x, ptr); \ + retval = __get_user_asm_w((void *)&x, \ + (long)ptr); \ break; \ case 4: \ - retval = __get_user_asm_l(x, ptr); \ + retval = __get_user_asm_l((void *)&x, \ + (long)ptr); \ break; \ case 8: \ - retval = __get_user_asm_q(x, ptr); \ + retval = __get_user_asm_q((void *)&x, \ + (long)ptr); \ break; \ default: \ __get_user_unknown(); \ @@ -54,16 +58,20 @@ do { \ retval = 0; \ switch (size) { \ case 1: \ - retval = __put_user_asm_b(x, ptr); \ + retval = __put_user_asm_b((void *)&x, \ + (long)ptr); \ break; \ case 2: \ - retval = __put_user_asm_w(x, ptr); \ + retval = __put_user_asm_w((void *)&x, \ + (long)ptr); \ break; \ case 4: \ - retval = __put_user_asm_l(x, ptr); \ + retval = __put_user_asm_l((void *)&x, \ + (long)ptr); \ break; \ case 8: \ - retval = __put_user_asm_q(x, ptr); \ + retval = __put_user_asm_q((void *)&x, \ + (long)ptr); \ break; \ default: \ __put_user_unknown(); \ -- cgit v1.2.3 From 50b72e600b62bcdf40971e55f609cf4771346cc1 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 29 Sep 2008 19:45:16 +0900 Subject: sh: sh_ksyms_64 needs __strncpy_from_user() definition. Signed-off-by: Paul Mundt --- arch/sh/include/asm/uaccess_64.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/uaccess_64.h b/arch/sh/include/asm/uaccess_64.h index 0042c9054b20..56fd20b8cdcc 100644 --- a/arch/sh/include/asm/uaccess_64.h +++ b/arch/sh/include/asm/uaccess_64.h @@ -85,5 +85,7 @@ extern long __put_user_asm_q(void *, long); extern void __put_user_unknown(void); extern long __strnlen_user(const char *__s, long __n); +extern int __strncpy_from_user(unsigned long __dest, + unsigned long __user __src, int __count); #endif /* __ASM_SH_UACCESS_64_H */ -- cgit v1.2.3 From 64c9627c2628bc3bd3291710b8ee6f8335883f8b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 1 Oct 2008 15:12:27 +0900 Subject: sh: Fix up the __raw_read/writeX() definitions. These were doing largely bogus things and using the wrong typing for the address. Bring these in line with the ARM definitions. Signed-off-by: Paul Mundt --- arch/sh/include/asm/io.h | 77 ++++++++++++++++++++++-------------------------- arch/sh/lib/io.c | 8 ++--- 2 files changed, 40 insertions(+), 45 deletions(-) (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h index 1857666136f2..d9e794eff830 100644 --- a/arch/sh/include/asm/io.h +++ b/arch/sh/include/asm/io.h @@ -101,44 +101,33 @@ #define outsw __outsw #define outsl __outsl -#define __raw_readb(a) __readb((void __iomem *)(a)) -#define __raw_readw(a) __readw((void __iomem *)(a)) -#define __raw_readl(a) __readl((void __iomem *)(a)) -#define __raw_writeb(v, a) __writeb(v, (void __iomem *)(a)) -#define __raw_writew(v, a) __writew(v, (void __iomem *)(a)) -#define __raw_writel(v, a) __writel(v, (void __iomem *)(a)) +#define __raw_writeb(v,a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a) = (v)) +#define __raw_writew(v,a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v)) +#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v)) -void __raw_writesl(unsigned long addr, const void *data, int longlen); -void __raw_readsl(unsigned long addr, void *data, int longlen); +#define __raw_readb(a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a)) +#define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a)) +#define __raw_readl(a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a)) + +void __raw_writesl(void __iomem *addr, const void *data, int longlen); +void __raw_readsl(const void __iomem *addr, void *data, int longlen); /* * The platform header files may define some of these macros to use * the inlined versions where appropriate. These macros may also be * redefined by userlevel programs. */ -#ifdef __readb -# define readb(a) ({ unsigned int r_ = __raw_readb(a); mb(); r_; }) -#endif -#ifdef __raw_readw -# define readw(a) ({ unsigned int r_ = __raw_readw(a); mb(); r_; }) -#endif -#ifdef __raw_readl -# define readl(a) ({ unsigned int r_ = __raw_readl(a); mb(); r_; }) -#endif +#define readb(a) ({ unsigned int r_ = __readb(a); mb(); r_; }) +#define readw(a) ({ unsigned int r_ = __readw(a); mb(); r_; }) +#define readl(a) ({ unsigned int r_ = __readl(a); mb(); r_; }) -#ifdef __raw_writeb -# define writeb(v,a) ({ __raw_writeb((v),(a)); mb(); }) -#endif -#ifdef __raw_writew -# define writew(v,a) ({ __raw_writew((v),(a)); mb(); }) -#endif -#ifdef __raw_writel -# define writel(v,a) ({ __raw_writel((v),(a)); mb(); }) -#endif +#define writeb(v,a) ({ __writeb((v),(a)); mb(); }) +#define writew(v,a) ({ __writew((v),(a)); mb(); }) +#define writel(v,a) ({ __writel((v),(a)); mb(); }) #define __BUILD_MEMORY_STRING(bwlq, type) \ \ -static inline void writes##bwlq(volatile void __iomem *mem, \ +static inline void __raw_writes##bwlq(volatile void __iomem *mem, \ const void *addr, unsigned int count) \ { \ const volatile type *__addr = addr; \ @@ -149,8 +138,8 @@ static inline void writes##bwlq(volatile void __iomem *mem, \ } \ } \ \ -static inline void reads##bwlq(volatile void __iomem *mem, void *addr, \ - unsigned int count) \ +static inline void __raw_reads##bwlq(volatile void __iomem *mem, \ + void *addr, unsigned int count) \ { \ volatile type *__addr = addr; \ \ @@ -162,7 +151,13 @@ static inline void reads##bwlq(volatile void __iomem *mem, void *addr, \ __BUILD_MEMORY_STRING(b, u8) __BUILD_MEMORY_STRING(w, u16) + +#define writesb __raw_writesb +#define writesw __raw_writesw #define writesl __raw_writesl + +#define readsb __raw_readsb +#define readsw __raw_readsw #define readsl __raw_readsl #define readb_relaxed(a) readb(a) @@ -170,25 +165,25 @@ __BUILD_MEMORY_STRING(w, u16) #define readl_relaxed(a) readl(a) /* Simple MMIO */ -#define ioread8(a) readb(a) -#define ioread16(a) readw(a) +#define ioread8(a) __raw_readb(a) +#define ioread16(a) __raw_readw(a) #define ioread16be(a) be16_to_cpu(__raw_readw((a))) -#define ioread32(a) readl(a) +#define ioread32(a) __raw_readl(a) #define ioread32be(a) be32_to_cpu(__raw_readl((a))) -#define iowrite8(v,a) writeb((v),(a)) -#define iowrite16(v,a) writew((v),(a)) +#define iowrite8(v,a) __raw_writeb((v),(a)) +#define iowrite16(v,a) __raw_writew((v),(a)) #define iowrite16be(v,a) __raw_writew(cpu_to_be16((v)),(a)) -#define iowrite32(v,a) writel((v),(a)) +#define iowrite32(v,a) __raw_writel((v),(a)) #define iowrite32be(v,a) __raw_writel(cpu_to_be32((v)),(a)) -#define ioread8_rep(a, d, c) readsb((a), (d), (c)) -#define ioread16_rep(a, d, c) readsw((a), (d), (c)) -#define ioread32_rep(a, d, c) readsl((a), (d), (c)) +#define ioread8_rep(a, d, c) __raw_readsb((a), (d), (c)) +#define ioread16_rep(a, d, c) __raw_readsw((a), (d), (c)) +#define ioread32_rep(a, d, c) __raw_readsl((a), (d), (c)) -#define iowrite8_rep(a, s, c) writesb((a), (s), (c)) -#define iowrite16_rep(a, s, c) writesw((a), (s), (c)) -#define iowrite32_rep(a, s, c) writesl((a), (s), (c)) +#define iowrite8_rep(a, s, c) __raw_writesb((a), (s), (c)) +#define iowrite16_rep(a, s, c) __raw_writesw((a), (s), (c)) +#define iowrite32_rep(a, s, c) __raw_writesl((a), (s), (c)) #define mmiowb() wmb() /* synco on SH-4A, otherwise a nop */ diff --git a/arch/sh/lib/io.c b/arch/sh/lib/io.c index 4f54ec43516f..88dfe6e396bc 100644 --- a/arch/sh/lib/io.c +++ b/arch/sh/lib/io.c @@ -14,12 +14,12 @@ #include #include -void __raw_readsl(unsigned long addr, void *datap, int len) +void __raw_readsl(const void __iomem *addr, void *datap, int len) { u32 *data; for (data = datap; (len != 0) && (((u32)data & 0x1f) != 0); len--) - *data++ = ctrl_inl(addr); + *data++ = __raw_readl(addr); if (likely(len >= (0x20 >> 2))) { int tmp2, tmp3, tmp4, tmp5, tmp6; @@ -59,11 +59,11 @@ void __raw_readsl(unsigned long addr, void *datap, int len) } for (; len != 0; len--) - *data++ = ctrl_inl(addr); + *data++ = __raw_readl(addr); } EXPORT_SYMBOL(__raw_readsl); -void __raw_writesl(unsigned long addr, const void *data, int len) +void __raw_writesl(void __iomem *addr, const void *data, int len) { if (likely(len != 0)) { int tmp1; -- cgit v1.2.3 From bbfbd8b151fe35c9a1180a7f5254c5d6b8387cc0 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 1 Oct 2008 16:13:54 +0900 Subject: sh: Move the shared INTC code out to drivers/sh/ The INTC code will be re-used across different architectures, so move this out to drivers/sh/ and include/linux/sh_intc.h respectively. Signed-off-by: Paul Mundt --- arch/sh/include/asm/hw_irq.h | 92 +----- arch/sh/kernel/cpu/irq/Makefile | 2 - arch/sh/kernel/cpu/irq/intc.c | 712 --------------------------------------- drivers/sh/Makefile | 2 +- drivers/sh/intc.c | 713 ++++++++++++++++++++++++++++++++++++++++ include/linux/sh_intc.h | 91 +++++ 6 files changed, 807 insertions(+), 805 deletions(-) delete mode 100644 arch/sh/kernel/cpu/irq/intc.c create mode 100644 drivers/sh/intc.c create mode 100644 include/linux/sh_intc.h (limited to 'arch/sh/include/asm') diff --git a/arch/sh/include/asm/hw_irq.h b/arch/sh/include/asm/hw_irq.h index d557b00111bf..603cdde813d1 100644 --- a/arch/sh/include/asm/hw_irq.h +++ b/arch/sh/include/asm/hw_irq.h @@ -2,6 +2,7 @@ #define __ASM_SH_HW_IRQ_H #include +#include #include extern atomic_t irq_err_count; @@ -23,101 +24,12 @@ struct ipr_desc { void register_ipr_controller(struct ipr_desc *); -typedef unsigned char intc_enum; - -struct intc_vect { - intc_enum enum_id; - unsigned short vect; -}; - -#define INTC_VECT(enum_id, vect) { enum_id, vect } -#define INTC_IRQ(enum_id, irq) INTC_VECT(enum_id, irq2evt(irq)) - -struct intc_group { - intc_enum enum_id; - intc_enum enum_ids[32]; -}; - -#define INTC_GROUP(enum_id, ids...) { enum_id, { ids } } - -struct intc_mask_reg { - unsigned long set_reg, clr_reg, reg_width; - intc_enum enum_ids[32]; -#ifdef CONFIG_SMP - unsigned long smp; -#endif -}; - -struct intc_prio_reg { - unsigned long set_reg, clr_reg, reg_width, field_width; - intc_enum enum_ids[16]; -#ifdef CONFIG_SMP - unsigned long smp; -#endif -}; - -struct intc_sense_reg { - unsigned long reg, reg_width, field_width; - intc_enum enum_ids[16]; -}; - -#ifdef CONFIG_SMP -#define INTC_SMP(stride, nr) .smp = (stride) | ((nr) << 8) -#else -#define INTC_SMP(stride, nr) -#endif - -struct intc_desc { - struct intc_vect *vectors; - unsigned int nr_vectors; - struct intc_group *groups; - unsigned int nr_groups; - struct intc_mask_reg *mask_regs; - unsigned int nr_mask_regs; - struct intc_prio_reg *prio_regs; - unsigned int nr_prio_regs; - struct intc_sense_reg *sense_regs; - unsigned int nr_sense_regs; - char *name; -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) - struct intc_mask_reg *ack_regs; - unsigned int nr_ack_regs; -#endif -}; - -#define _INTC_ARRAY(a) a, sizeof(a)/sizeof(*a) -#define DECLARE_INTC_DESC(symbol, chipname, vectors, groups, \ - mask_regs, prio_regs, sense_regs) \ -struct intc_desc symbol __initdata = { \ - _INTC_ARRAY(vectors), _INTC_ARRAY(groups), \ - _INTC_ARRAY(mask_regs), _INTC_ARRAY(prio_regs), \ - _INTC_ARRAY(sense_regs), \ - chipname, \ -} - -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) -#define DECLARE_INTC_DESC_ACK(symbol, chipname, vectors, groups, \ - mask_regs, prio_regs, sense_regs, ack_regs) \ -struct intc_desc symbol __initdata = { \ - _INTC_ARRAY(vectors), _INTC_ARRAY(groups), \ - _INTC_ARRAY(mask_regs), _INTC_ARRAY(prio_regs), \ - _INTC_ARRAY(sense_regs), \ - chipname, \ - _INTC_ARRAY(ack_regs), \ -} -#endif - -void __init register_intc_controller(struct intc_desc *desc); -int intc_set_priority(unsigned int irq, unsigned int prio); - void __init plat_irq_setup(void); -#ifdef CONFIG_CPU_SH3 void __init plat_irq_setup_sh3(void); -#endif +void __init plat_irq_setup_pins(int mode); enum { IRQ_MODE_IRQ, IRQ_MODE_IRQ7654, IRQ_MODE_IRQ3210, IRQ_MODE_IRL7654_MASK, IRQ_MODE_IRL3210_MASK, IRQ_MODE_IRL7654, IRQ_MODE_IRL3210 }; -void __init plat_irq_setup_pins(int mode); #endif /* __ASM_SH_HW_IRQ_H */ diff --git a/arch/sh/kernel/cpu/irq/Makefile b/arch/sh/kernel/cpu/irq/Makefile index 462a8f6dfee2..f0c7025a67d1 100644 --- a/arch/sh/kernel/cpu/irq/Makefile +++ b/arch/sh/kernel/cpu/irq/Makefile @@ -1,8 +1,6 @@ # # Makefile for the Linux/SuperH CPU-specifc IRQ handlers. # -obj-y += intc.o - obj-$(CONFIG_SUPERH32) += imask.o obj-$(CONFIG_CPU_SH5) += intc-sh5.o obj-$(CONFIG_CPU_HAS_IPR_IRQ) += ipr.o diff --git a/arch/sh/kernel/cpu/irq/intc.c b/arch/sh/kernel/cpu/irq/intc.c deleted file mode 100644 index 138efa4e95db..000000000000 --- a/arch/sh/kernel/cpu/irq/intc.c +++ /dev/null @@ -1,712 +0,0 @@ -/* - * Shared interrupt handling code for IPR and INTC2 types of IRQs. - * - * Copyright (C) 2007, 2008 Magnus Damm - * - * Based on intc2.c and ipr.c - * - * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi - * Copyright (C) 2000 Kazumoto Kojima - * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) - * Copyright (C) 2003 Takashi Kusuda - * Copyright (C) 2005, 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 -#include -#include -#include -#include -#include - -#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ - ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ - ((addr_e) << 16) | ((addr_d << 24))) - -#define _INTC_SHIFT(h) (h & 0x1f) -#define _INTC_WIDTH(h) ((h >> 5) & 0xf) -#define _INTC_FN(h) ((h >> 9) & 0xf) -#define _INTC_MODE(h) ((h >> 13) & 0x7) -#define _INTC_ADDR_E(h) ((h >> 16) & 0xff) -#define _INTC_ADDR_D(h) ((h >> 24) & 0xff) - -struct intc_handle_int { - unsigned int irq; - unsigned long handle; -}; - -struct intc_desc_int { - unsigned long *reg; -#ifdef CONFIG_SMP - unsigned long *smp; -#endif - unsigned int nr_reg; - struct intc_handle_int *prio; - unsigned int nr_prio; - struct intc_handle_int *sense; - unsigned int nr_sense; - struct irq_chip chip; -}; - -#ifdef CONFIG_SMP -#define IS_SMP(x) x.smp -#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c)) -#define SMP_NR(d, x) ((d->smp[(x)] >> 8) ? (d->smp[(x)] >> 8) : 1) -#else -#define IS_SMP(x) 0 -#define INTC_REG(d, x, c) (d->reg[(x)]) -#define SMP_NR(d, x) 1 -#endif - -static unsigned int intc_prio_level[NR_IRQS]; /* for now */ -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) -static unsigned long ack_handle[NR_IRQS]; -#endif - -static inline struct intc_desc_int *get_intc_desc(unsigned int irq) -{ - struct irq_chip *chip = get_irq_chip(irq); - return (void *)((char *)chip - offsetof(struct intc_desc_int, chip)); -} - -static inline unsigned int set_field(unsigned int value, - unsigned int field_value, - unsigned int handle) -{ - unsigned int width = _INTC_WIDTH(handle); - unsigned int shift = _INTC_SHIFT(handle); - - value &= ~(((1 << width) - 1) << shift); - value |= field_value << shift; - return value; -} - -static void write_8(unsigned long addr, unsigned long h, unsigned long data) -{ - __raw_writeb(set_field(0, data, h), addr); -} - -static void write_16(unsigned long addr, unsigned long h, unsigned long data) -{ - __raw_writew(set_field(0, data, h), addr); -} - -static void write_32(unsigned long addr, unsigned long h, unsigned long data) -{ - __raw_writel(set_field(0, data, h), addr); -} - -static void modify_8(unsigned long addr, unsigned long h, unsigned long data) -{ - unsigned long flags; - local_irq_save(flags); - __raw_writeb(set_field(__raw_readb(addr), data, h), addr); - local_irq_restore(flags); -} - -static void modify_16(unsigned long addr, unsigned long h, unsigned long data) -{ - unsigned long flags; - local_irq_save(flags); - __raw_writew(set_field(__raw_readw(addr), data, h), addr); - local_irq_restore(flags); -} - -static void modify_32(unsigned long addr, unsigned long h, unsigned long data) -{ - unsigned long flags; - local_irq_save(flags); - __raw_writel(set_field(__raw_readl(addr), data, h), addr); - local_irq_restore(flags); -} - -enum { REG_FN_ERR = 0, REG_FN_WRITE_BASE = 1, REG_FN_MODIFY_BASE = 5 }; - -static void (*intc_reg_fns[])(unsigned long addr, - unsigned long h, - unsigned long data) = { - [REG_FN_WRITE_BASE + 0] = write_8, - [REG_FN_WRITE_BASE + 1] = write_16, - [REG_FN_WRITE_BASE + 3] = write_32, - [REG_FN_MODIFY_BASE + 0] = modify_8, - [REG_FN_MODIFY_BASE + 1] = modify_16, - [REG_FN_MODIFY_BASE + 3] = modify_32, -}; - -enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */ - MODE_MASK_REG, /* Bit(s) set -> interrupt disabled */ - MODE_DUAL_REG, /* Two registers, set bit to enable / disable */ - MODE_PRIO_REG, /* Priority value written to enable interrupt */ - MODE_PCLR_REG, /* Above plus all bits set to disable interrupt */ -}; - -static void intc_mode_field(unsigned long addr, - unsigned long handle, - void (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) -{ - fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1)); -} - -static void intc_mode_zero(unsigned long addr, - unsigned long handle, - void (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) -{ - fn(addr, handle, 0); -} - -static void intc_mode_prio(unsigned long addr, - unsigned long handle, - void (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) -{ - fn(addr, handle, intc_prio_level[irq]); -} - -static void (*intc_enable_fns[])(unsigned long addr, - unsigned long handle, - void (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) = { - [MODE_ENABLE_REG] = intc_mode_field, - [MODE_MASK_REG] = intc_mode_zero, - [MODE_DUAL_REG] = intc_mode_field, - [MODE_PRIO_REG] = intc_mode_prio, - [MODE_PCLR_REG] = intc_mode_prio, -}; - -static void (*intc_disable_fns[])(unsigned long addr, - unsigned long handle, - void (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) = { - [MODE_ENABLE_REG] = intc_mode_zero, - [MODE_MASK_REG] = intc_mode_field, - [MODE_DUAL_REG] = intc_mode_field, - [MODE_PRIO_REG] = intc_mode_zero, - [MODE_PCLR_REG] = intc_mode_field, -}; - -static inline void _intc_enable(unsigned int irq, unsigned long handle) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long addr; - unsigned int cpu; - - for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { - addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); - intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\ - [_INTC_FN(handle)], irq); - } -} - -static void intc_enable(unsigned int irq) -{ - _intc_enable(irq, (unsigned long)get_irq_chip_data(irq)); -} - -static void intc_disable(unsigned int irq) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = (unsigned long) get_irq_chip_data(irq); - unsigned long addr; - unsigned int cpu; - - for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { - addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); - intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\ - [_INTC_FN(handle)], irq); - } -} - -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) -static void intc_mask_ack(unsigned int irq) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = ack_handle[irq]; - unsigned long addr; - - intc_disable(irq); - - /* read register and write zero only to the assocaited bit */ - - if (handle) { - addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); - switch (_INTC_FN(handle)) { - case REG_FN_MODIFY_BASE + 0: /* 8bit */ - __raw_readb(addr); - __raw_writeb(0xff ^ set_field(0, 1, handle), addr); - break; - case REG_FN_MODIFY_BASE + 1: /* 16bit */ - __raw_readw(addr); - __raw_writew(0xffff ^ set_field(0, 1, handle), addr); - break; - case REG_FN_MODIFY_BASE + 3: /* 32bit */ - __raw_readl(addr); - __raw_writel(0xffffffff ^ set_field(0, 1, handle), addr); - break; - default: - BUG(); - break; - } - } -} -#endif - -static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, - unsigned int nr_hp, - unsigned int irq) -{ - int i; - - /* this doesn't scale well, but... - * - * this function should only be used for cerain uncommon - * operations such as intc_set_priority() and intc_set_sense() - * and in those rare cases performance doesn't matter that much. - * keeping the memory footprint low is more important. - * - * one rather simple way to speed this up and still keep the - * memory footprint down is to make sure the array is sorted - * and then perform a bisect to lookup the irq. - */ - - for (i = 0; i < nr_hp; i++) { - if ((hp + i)->irq != irq) - continue; - - return hp + i; - } - - return NULL; -} - -int intc_set_priority(unsigned int irq, unsigned int prio) -{ - struct intc_desc_int *d = get_intc_desc(irq); - struct intc_handle_int *ihp; - - if (!intc_prio_level[irq] || prio <= 1) - return -EINVAL; - - ihp = intc_find_irq(d->prio, d->nr_prio, irq); - if (ihp) { - if (prio >= (1 << _INTC_WIDTH(ihp->handle))) - return -EINVAL; - - intc_prio_level[irq] = prio; - - /* - * only set secondary masking method directly - * primary masking method is using intc_prio_level[irq] - * priority level will be set during next enable() - */ - - if (_INTC_FN(ihp->handle) != REG_FN_ERR) - _intc_enable(irq, ihp->handle); - } - return 0; -} - -#define VALID(x) (x | 0x80) - -static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = { - [IRQ_TYPE_EDGE_FALLING] = VALID(0), - [IRQ_TYPE_EDGE_RISING] = VALID(1), - [IRQ_TYPE_LEVEL_LOW] = VALID(2), - /* SH7706, SH7707 and SH7709 do not support high level triggered */ -#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \ - !defined(CONFIG_CPU_SUBTYPE_SH7707) && \ - !defined(CONFIG_CPU_SUBTYPE_SH7709) - [IRQ_TYPE_LEVEL_HIGH] = VALID(3), -#endif -}; - -static int intc_set_sense(unsigned int irq, unsigned int type) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK]; - struct intc_handle_int *ihp; - unsigned long addr; - - if (!value) - return -EINVAL; - - ihp = intc_find_irq(d->sense, d->nr_sense, irq); - if (ihp) { - addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0); - intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value); - } - return 0; -} - -static unsigned int __init intc_get_reg(struct intc_desc_int *d, - unsigned long address) -{ - unsigned int k; - - for (k = 0; k < d->nr_reg; k++) { - if (d->reg[k] == address) - return k; - } - - BUG(); - return 0; -} - -static intc_enum __init intc_grp_id(struct intc_desc *desc, - intc_enum enum_id) -{ - struct intc_group *g = desc->groups; - unsigned int i, j; - - for (i = 0; g && enum_id && i < desc->nr_groups; i++) { - g = desc->groups + i; - - for (j = 0; g->enum_ids[j]; j++) { - if (g->enum_ids[j] != enum_id) - continue; - - return g->enum_id; - } - } - - return 0; -} - -static unsigned int __init intc_mask_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, int do_grps) -{ - struct intc_mask_reg *mr = desc->mask_regs; - unsigned int i, j, fn, mode; - unsigned long reg_e, reg_d; - - for (i = 0; mr && enum_id && i < desc->nr_mask_regs; i++) { - mr = desc->mask_regs + i; - - for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { - if (mr->enum_ids[j] != enum_id) - continue; - - if (mr->set_reg && mr->clr_reg) { - fn = REG_FN_WRITE_BASE; - mode = MODE_DUAL_REG; - reg_e = mr->clr_reg; - reg_d = mr->set_reg; - } else { - fn = REG_FN_MODIFY_BASE; - if (mr->set_reg) { - mode = MODE_ENABLE_REG; - reg_e = mr->set_reg; - reg_d = mr->set_reg; - } else { - mode = MODE_MASK_REG; - reg_e = mr->clr_reg; - reg_d = mr->clr_reg; - } - } - - fn += (mr->reg_width >> 3) - 1; - return _INTC_MK(fn, mode, - intc_get_reg(d, reg_e), - intc_get_reg(d, reg_d), - 1, - (mr->reg_width - 1) - j); - } - } - - if (do_grps) - return intc_mask_data(desc, d, intc_grp_id(desc, enum_id), 0); - - return 0; -} - -static unsigned int __init intc_prio_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, int do_grps) -{ - struct intc_prio_reg *pr = desc->prio_regs; - unsigned int i, j, fn, mode, bit; - unsigned long reg_e, reg_d; - - for (i = 0; pr && enum_id && i < desc->nr_prio_regs; i++) { - pr = desc->prio_regs + i; - - for (j = 0; j < ARRAY_SIZE(pr->enum_ids); j++) { - if (pr->enum_ids[j] != enum_id) - continue; - - if (pr->set_reg && pr->clr_reg) { - fn = REG_FN_WRITE_BASE; - mode = MODE_PCLR_REG; - reg_e = pr->set_reg; - reg_d = pr->clr_reg; - } else { - fn = REG_FN_MODIFY_BASE; - mode = MODE_PRIO_REG; - if (!pr->set_reg) - BUG(); - reg_e = pr->set_reg; - reg_d = pr->set_reg; - } - - fn += (pr->reg_width >> 3) - 1; - - BUG_ON((j + 1) * pr->field_width > pr->reg_width); - - bit = pr->reg_width - ((j + 1) * pr->field_width); - - return _INTC_MK(fn, mode, - intc_get_reg(d, reg_e), - intc_get_reg(d, reg_d), - pr->field_width, bit); - } - } - - if (do_grps) - return intc_prio_data(desc, d, intc_grp_id(desc, enum_id), 0); - - return 0; -} - -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) -static unsigned int __init intc_ack_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id) -{ - struct intc_mask_reg *mr = desc->ack_regs; - unsigned int i, j, fn, mode; - unsigned long reg_e, reg_d; - - for (i = 0; mr && enum_id && i < desc->nr_ack_regs; i++) { - mr = desc->ack_regs + i; - - for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { - if (mr->enum_ids[j] != enum_id) - continue; - - fn = REG_FN_MODIFY_BASE; - mode = MODE_ENABLE_REG; - reg_e = mr->set_reg; - reg_d = mr->set_reg; - - fn += (mr->reg_width >> 3) - 1; - return _INTC_MK(fn, mode, - intc_get_reg(d, reg_e), - intc_get_reg(d, reg_d), - 1, - (mr->reg_width - 1) - j); - } - } - - return 0; -} -#endif - -static unsigned int __init intc_sense_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id) -{ - struct intc_sense_reg *sr = desc->sense_regs; - unsigned int i, j, fn, bit; - - for (i = 0; sr && enum_id && i < desc->nr_sense_regs; i++) { - sr = desc->sense_regs + i; - - for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) { - if (sr->enum_ids[j] != enum_id) - continue; - - fn = REG_FN_MODIFY_BASE; - fn += (sr->reg_width >> 3) - 1; - - BUG_ON((j + 1) * sr->field_width > sr->reg_width); - - bit = sr->reg_width - ((j + 1) * sr->field_width); - - return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg), - 0, sr->field_width, bit); - } - } - - return 0; -} - -static void __init intc_register_irq(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, - unsigned int irq) -{ - struct intc_handle_int *hp; - unsigned int data[2], primary; - - /* Prefer single interrupt source bitmap over other combinations: - * 1. bitmap, single interrupt source - * 2. priority, single interrupt source - * 3. bitmap, multiple interrupt sources (groups) - * 4. priority, multiple interrupt sources (groups) - */ - - data[0] = intc_mask_data(desc, d, enum_id, 0); - data[1] = intc_prio_data(desc, d, enum_id, 0); - - primary = 0; - if (!data[0] && data[1]) - primary = 1; - - data[0] = data[0] ? data[0] : intc_mask_data(desc, d, enum_id, 1); - data[1] = data[1] ? data[1] : intc_prio_data(desc, d, enum_id, 1); - - if (!data[primary]) - primary ^= 1; - - BUG_ON(!data[primary]); /* must have primary masking method */ - - disable_irq_nosync(irq); - set_irq_chip_and_handler_name(irq, &d->chip, - handle_level_irq, "level"); - set_irq_chip_data(irq, (void *)data[primary]); - - /* set priority level - * - this needs to be at least 2 for 5-bit priorities on 7780 - */ - intc_prio_level[irq] = 2; - - /* enable secondary masking method if present */ - if (data[!primary]) - _intc_enable(irq, data[!primary]); - - /* add irq to d->prio list if priority is available */ - if (data[1]) { - hp = d->prio + d->nr_prio; - hp->irq = irq; - hp->handle = data[1]; - - if (primary) { - /* - * only secondary priority should access registers, so - * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() - */ - - hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); - hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); - } - d->nr_prio++; - } - - /* add irq to d->sense list if sense is available */ - data[0] = intc_sense_data(desc, d, enum_id); - if (data[0]) { - (d->sense + d->nr_sense)->irq = irq; - (d->sense + d->nr_sense)->handle = data[0]; - d->nr_sense++; - } - - /* irq should be disabled by default */ - d->chip.mask(irq); - -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) - if (desc->ack_regs) - ack_handle[irq] = intc_ack_data(desc, d, enum_id); -#endif -} - -static unsigned int __init save_reg(struct intc_desc_int *d, - unsigned int cnt, - unsigned long value, - unsigned int smp) -{ - if (value) { - d->reg[cnt] = value; -#ifdef CONFIG_SMP - d->smp[cnt] = smp; -#endif - return 1; - } - - return 0; -} - - -void __init register_intc_controller(struct intc_desc *desc) -{ - unsigned int i, k, smp; - struct intc_desc_int *d; - - d = alloc_bootmem(sizeof(*d)); - - d->nr_reg = desc->mask_regs ? desc->nr_mask_regs * 2 : 0; - d->nr_reg += desc->prio_regs ? desc->nr_prio_regs * 2 : 0; - d->nr_reg += desc->sense_regs ? desc->nr_sense_regs : 0; - -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) - d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0; -#endif - d->reg = alloc_bootmem(d->nr_reg * sizeof(*d->reg)); -#ifdef CONFIG_SMP - d->smp = alloc_bootmem(d->nr_reg * sizeof(*d->smp)); -#endif - k = 0; - - if (desc->mask_regs) { - for (i = 0; i < desc->nr_mask_regs; i++) { - smp = IS_SMP(desc->mask_regs[i]); - k += save_reg(d, k, desc->mask_regs[i].set_reg, smp); - k += save_reg(d, k, desc->mask_regs[i].clr_reg, smp); - } - } - - if (desc->prio_regs) { - d->prio = alloc_bootmem(desc->nr_vectors * sizeof(*d->prio)); - - for (i = 0; i < desc->nr_prio_regs; i++) { - smp = IS_SMP(desc->prio_regs[i]); - k += save_reg(d, k, desc->prio_regs[i].set_reg, smp); - k += save_reg(d, k, desc->prio_regs[i].clr_reg, smp); - } - } - - if (desc->sense_regs) { - d->sense = alloc_bootmem(desc->nr_vectors * sizeof(*d->sense)); - - for (i = 0; i < desc->nr_sense_regs; i++) { - k += save_reg(d, k, desc->sense_regs[i].reg, 0); - } - } - - d->chip.name = desc->name; - d->chip.mask = intc_disable; - d->chip.unmask = intc_enable; - d->chip.mask_ack = intc_disable; - d->chip.set_type = intc_set_sense; - -#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) - if (desc->ack_regs) { - for (i = 0; i < desc->nr_ack_regs; i++) - k += save_reg(d, k, desc->ack_regs[i].set_reg, 0); - - d->chip.mask_ack = intc_mask_ack; - } -#endif - - BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ - - for (i = 0; i < desc->nr_vectors; i++) { - struct intc_vect *vect = desc->vectors + i; - - intc_register_irq(desc, d, vect->enum_id, evt2irq(vect->vect)); - } -} diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index a96f4a8cfeb8..6a025cefe6dc 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -1,6 +1,6 @@ # # Makefile for the SuperH specific drivers. # - obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_MAPLE) += maple/ +obj-y += intc.o diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c new file mode 100644 index 000000000000..58d24c5a76ce --- /dev/null +++ b/drivers/sh/intc.c @@ -0,0 +1,713 @@ +/* + * Shared interrupt handling code for IPR and INTC2 types of IRQs. + * + * Copyright (C) 2007, 2008 Magnus Damm + * + * Based on intc2.c and ipr.c + * + * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi + * Copyright (C) 2000 Kazumoto Kojima + * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) + * Copyright (C) 2003 Takashi Kusuda + * Copyright (C) 2005, 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 +#include +#include +#include +#include +#include +#include + +#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ + ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ + ((addr_e) << 16) | ((addr_d << 24))) + +#define _INTC_SHIFT(h) (h & 0x1f) +#define _INTC_WIDTH(h) ((h >> 5) & 0xf) +#define _INTC_FN(h) ((h >> 9) & 0xf) +#define _INTC_MODE(h) ((h >> 13) & 0x7) +#define _INTC_ADDR_E(h) ((h >> 16) & 0xff) +#define _INTC_ADDR_D(h) ((h >> 24) & 0xff) + +struct intc_handle_int { + unsigned int irq; + unsigned long handle; +}; + +struct intc_desc_int { + unsigned long *reg; +#ifdef CONFIG_SMP + unsigned long *smp; +#endif + unsigned int nr_reg; + struct intc_handle_int *prio; + unsigned int nr_prio; + struct intc_handle_int *sense; + unsigned int nr_sense; + struct irq_chip chip; +}; + +#ifdef CONFIG_SMP +#define IS_SMP(x) x.smp +#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c)) +#define SMP_NR(d, x) ((d->smp[(x)] >> 8) ? (d->smp[(x)] >> 8) : 1) +#else +#define IS_SMP(x) 0 +#define INTC_REG(d, x, c) (d->reg[(x)]) +#define SMP_NR(d, x) 1 +#endif + +static unsigned int intc_prio_level[NR_IRQS]; /* for now */ +#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) +static unsigned long ack_handle[NR_IRQS]; +#endif + +static inline struct intc_desc_int *get_intc_desc(unsigned int irq) +{ + struct irq_chip *chip = get_irq_chip(irq); + return (void *)((char *)chip - offsetof(struct intc_desc_int, chip)); +} + +static inline unsigned int set_field(unsigned int value, + unsigned int field_value, + unsigned int handle) +{ + unsigned int width = _INTC_WIDTH(handle); + unsigned int shift = _INTC_SHIFT(handle); + + value &= ~(((1 << width) - 1) << shift); + value |= field_value << shift; + return value; +} + +static void write_8(unsigned long addr, unsigned long h, unsigned long data) +{ + __raw_writeb(set_field(0, data, h), addr); +} + +static void write_16(unsigned long addr, unsigned long h, unsigned long data) +{ + __raw_writew(set_field(0, data, h), addr); +} + +static void write_32(unsigned long addr, unsigned long h, unsigned long data) +{ + __raw_writel(set_field(0, data, h), addr); +} + +static void modify_8(unsigned long addr, unsigned long h, unsigned long data) +{ + unsigned long flags; + local_irq_save(flags); + __raw_writeb(set_field(__raw_readb(addr), data, h), addr); + local_irq_restore(flags); +} + +static void modify_16(unsigned long addr, unsigned long h, unsigned long data) +{ + unsigned long flags; + local_irq_save(flags); + __raw_writew(set_field(__raw_readw(addr), data, h), addr); + local_irq_restore(flags); +} + +static void modify_32(unsigned long addr, unsigned long h, unsigned long data) +{ + unsigned long flags; + local_irq_save(flags); + __raw_writel(set_field(__raw_readl(addr), data, h), addr); + local_irq_restore(flags); +} + +enum { REG_FN_ERR = 0, REG_FN_WRITE_BASE = 1, REG_FN_MODIFY_BASE = 5 }; + +static void (*intc_reg_fns[])(unsigned long addr, + unsigned long h, + unsigned long data) = { + [REG_FN_WRITE_BASE + 0] = write_8, + [REG_FN_WRITE_BASE + 1] = write_16, + [REG_FN_WRITE_BASE + 3] = write_32, + [REG_FN_MODIFY_BASE + 0] = modify_8, + [REG_FN_MODIFY_BASE + 1] = modify_16, + [REG_FN_MODIFY_BASE + 3] = modify_32, +}; + +enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */ + MODE_MASK_REG, /* Bit(s) set -> interrupt disabled */ + MODE_DUAL_REG, /* Two registers, set bit to enable / disable */ + MODE_PRIO_REG, /* Priority value written to enable interrupt */ + MODE_PCLR_REG, /* Above plus all bits set to disable interrupt */ +}; + +static void intc_mode_field(unsigned long addr, + unsigned long handle, + void (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) +{ + fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1)); +} + +static void intc_mode_zero(unsigned long addr, + unsigned long handle, + void (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) +{ + fn(addr, handle, 0); +} + +static void intc_mode_prio(unsigned long addr, + unsigned long handle, + void (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) +{ + fn(addr, handle, intc_prio_level[irq]); +} + +static void (*intc_enable_fns[])(unsigned long addr, + unsigned long handle, + void (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) = { + [MODE_ENABLE_REG] = intc_mode_field, + [MODE_MASK_REG] = intc_mode_zero, + [MODE_DUAL_REG] = intc_mode_field, + [MODE_PRIO_REG] = intc_mode_prio, + [MODE_PCLR_REG] = intc_mode_prio, +}; + +static void (*intc_disable_fns[])(unsigned long addr, + unsigned long handle, + void (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) = { + [MODE_ENABLE_REG] = intc_mode_zero, + [MODE_MASK_REG] = intc_mode_field, + [MODE_DUAL_REG] = intc_mode_field, + [MODE_PRIO_REG] = intc_mode_zero, + [MODE_PCLR_REG] = intc_mode_field, +}; + +static inline void _intc_enable(unsigned int irq, unsigned long handle) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long addr; + unsigned int cpu; + + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { + addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); + intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\ + [_INTC_FN(handle)], irq); + } +} + +static void intc_enable(unsigned int irq) +{ + _intc_enable(irq, (unsigned long)get_irq_chip_data(irq)); +} + +static void intc_disable(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = (unsigned long) get_irq_chip_data(irq); + unsigned long addr; + unsigned int cpu; + + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { + addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); + intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\ + [_INTC_FN(handle)], irq); + } +} + +#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) +static void intc_mask_ack(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = ack_handle[irq]; + unsigned long addr; + + intc_disable(irq); + + /* read register and write zero only to the assocaited bit */ + + if (handle) { + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + switch (_INTC_FN(handle)) { + case REG_FN_MODIFY_BASE + 0: /* 8bit */ + __raw_readb(addr); + __raw_writeb(0xff ^ set_field(0, 1, handle), addr); + break; + case REG_FN_MODIFY_BASE + 1: /* 16bit */ + __raw_readw(addr); + __raw_writew(0xffff ^ set_field(0, 1, handle), addr); + break; + case REG_FN_MODIFY_BASE + 3: /* 32bit */ + __raw_readl(addr); + __raw_writel(0xffffffff ^ set_field(0, 1, handle), addr); + break; + default: + BUG(); + break; + } + } +} +#endif + +static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, + unsigned int nr_hp, + unsigned int irq) +{ + int i; + + /* this doesn't scale well, but... + * + * this function should only be used for cerain uncommon + * operations such as intc_set_priority() and intc_set_sense() + * and in those rare cases performance doesn't matter that much. + * keeping the memory footprint low is more important. + * + * one rather simple way to speed this up and still keep the + * memory footprint down is to make sure the array is sorted + * and then perform a bisect to lookup the irq. + */ + + for (i = 0; i < nr_hp; i++) { + if ((hp + i)->irq != irq) + continue; + + return hp + i; + } + + return NULL; +} + +int intc_set_priority(unsigned int irq, unsigned int prio) +{ + struct intc_desc_int *d = get_intc_desc(irq); + struct intc_handle_int *ihp; + + if (!intc_prio_level[irq] || prio <= 1) + return -EINVAL; + + ihp = intc_find_irq(d->prio, d->nr_prio, irq); + if (ihp) { + if (prio >= (1 << _INTC_WIDTH(ihp->handle))) + return -EINVAL; + + intc_prio_level[irq] = prio; + + /* + * only set secondary masking method directly + * primary masking method is using intc_prio_level[irq] + * priority level will be set during next enable() + */ + + if (_INTC_FN(ihp->handle) != REG_FN_ERR) + _intc_enable(irq, ihp->handle); + } + return 0; +} + +#define VALID(x) (x | 0x80) + +static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = { + [IRQ_TYPE_EDGE_FALLING] = VALID(0), + [IRQ_TYPE_EDGE_RISING] = VALID(1), + [IRQ_TYPE_LEVEL_LOW] = VALID(2), + /* SH7706, SH7707 and SH7709 do not support high level triggered */ +#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \ + !defined(CONFIG_CPU_SUBTYPE_SH7707) && \ + !defined(CONFIG_CPU_SUBTYPE_SH7709) + [IRQ_TYPE_LEVEL_HIGH] = VALID(3), +#endif +}; + +static int intc_set_sense(unsigned int irq, unsigned int type) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK]; + struct intc_handle_int *ihp; + unsigned long addr; + + if (!value) + return -EINVAL; + + ihp = intc_find_irq(d->sense, d->nr_sense, irq); + if (ihp) { + addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0); + intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value); + } + return 0; +} + +static unsigned int __init intc_get_reg(struct intc_desc_int *d, + unsigned long address) +{ + unsigned int k; + + for (k = 0; k < d->nr_reg; k++) { + if (d->reg[k] == address) + return k; + } + + BUG(); + return 0; +} + +static intc_enum __init intc_grp_id(struct intc_desc *desc, + intc_enum enum_id) +{ + struct intc_group *g = desc->groups; + unsigned int i, j; + + for (i = 0; g && enum_id && i < desc->nr_groups; i++) { + g = desc->groups + i; + + for (j = 0; g->enum_ids[j]; j++) { + if (g->enum_ids[j] != enum_id) + continue; + + return g->enum_id; + } + } + + return 0; +} + +static unsigned int __init intc_mask_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, int do_grps) +{ + struct intc_mask_reg *mr = desc->mask_regs; + unsigned int i, j, fn, mode; + unsigned long reg_e, reg_d; + + for (i = 0; mr && enum_id && i < desc->nr_mask_regs; i++) { + mr = desc->mask_regs + i; + + for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { + if (mr->enum_ids[j] != enum_id) + continue; + + if (mr->set_reg && mr->clr_reg) { + fn = REG_FN_WRITE_BASE; + mode = MODE_DUAL_REG; + reg_e = mr->clr_reg; + reg_d = mr->set_reg; + } else { + fn = REG_FN_MODIFY_BASE; + if (mr->set_reg) { + mode = MODE_ENABLE_REG; + reg_e = mr->set_reg; + reg_d = mr->set_reg; + } else { + mode = MODE_MASK_REG; + reg_e = mr->clr_reg; + reg_d = mr->clr_reg; + } + } + + fn += (mr->reg_width >> 3) - 1; + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + 1, + (mr->reg_width - 1) - j); + } + } + + if (do_grps) + return intc_mask_data(desc, d, intc_grp_id(desc, enum_id), 0); + + return 0; +} + +static unsigned int __init intc_prio_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, int do_grps) +{ + struct intc_prio_reg *pr = desc->prio_regs; + unsigned int i, j, fn, mode, bit; + unsigned long reg_e, reg_d; + + for (i = 0; pr && enum_id && i < desc->nr_prio_regs; i++) { + pr = desc->prio_regs + i; + + for (j = 0; j < ARRAY_SIZE(pr->enum_ids); j++) { + if (pr->enum_ids[j] != enum_id) + continue; + + if (pr->set_reg && pr->clr_reg) { + fn = REG_FN_WRITE_BASE; + mode = MODE_PCLR_REG; + reg_e = pr->set_reg; + reg_d = pr->clr_reg; + } else { + fn = REG_FN_MODIFY_BASE; + mode = MODE_PRIO_REG; + if (!pr->set_reg) + BUG(); + reg_e = pr->set_reg; + reg_d = pr->set_reg; + } + + fn += (pr->reg_width >> 3) - 1; + + BUG_ON((j + 1) * pr->field_width > pr->reg_width); + + bit = pr->reg_width - ((j + 1) * pr->field_width); + + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + pr->field_width, bit); + } + } + + if (do_grps) + return intc_prio_data(desc, d, intc_grp_id(desc, enum_id), 0); + + return 0; +} + +#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) +static unsigned int __init intc_ack_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id) +{ + struct intc_mask_reg *mr = desc->ack_regs; + unsigned int i, j, fn, mode; + unsigned long reg_e, reg_d; + + for (i = 0; mr && enum_id && i < desc->nr_ack_regs; i++) { + mr = desc->ack_regs + i; + + for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { + if (mr->enum_ids[j] != enum_id) + continue; + + fn = REG_FN_MODIFY_BASE; + mode = MODE_ENABLE_REG; + reg_e = mr->set_reg; + reg_d = mr->set_reg; + + fn += (mr->reg_width >> 3) - 1; + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + 1, + (mr->reg_width - 1) - j); + } + } + + return 0; +} +#endif + +static unsigned int __init intc_sense_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id) +{ + struct intc_sense_reg *sr = desc->sense_regs; + unsigned int i, j, fn, bit; + + for (i = 0; sr && enum_id && i < desc->nr_sense_regs; i++) { + sr = desc->sense_regs + i; + + for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) { + if (sr->enum_ids[j] != enum_id) + continue; + + fn = REG_FN_MODIFY_BASE; + fn += (sr->reg_width >> 3) - 1; + + BUG_ON((j + 1) * sr->field_width > sr->reg_width); + + bit = sr->reg_width - ((j + 1) * sr->field_width); + + return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg), + 0, sr->field_width, bit); + } + } + + return 0; +} + +static void __init intc_register_irq(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, + unsigned int irq) +{ + struct intc_handle_int *hp; + unsigned int data[2], primary; + + /* Prefer single interrupt source bitmap over other combinations: + * 1. bitmap, single interrupt source + * 2. priority, single interrupt source + * 3. bitmap, multiple interrupt sources (groups) + * 4. priority, multiple interrupt sources (groups) + */ + + data[0] = intc_mask_data(desc, d, enum_id, 0); + data[1] = intc_prio_data(desc, d, enum_id, 0); + + primary = 0; + if (!data[0] && data[1]) + primary = 1; + + data[0] = data[0] ? data[0] : intc_mask_data(desc, d, enum_id, 1); + data[1] = data[1] ? data[1] : intc_prio_data(desc, d, enum_id, 1); + + if (!data[primary]) + primary ^= 1; + + BUG_ON(!data[primary]); /* must have primary masking method */ + + disable_irq_nosync(irq); + set_irq_chip_and_handler_name(irq, &d->chip, + handle_level_irq, "level"); + set_irq_chip_data(irq, (void *)data[primary]); + + /* set priority level + * - this needs to be at least 2 for 5-bit priorities on 7780 + */ + intc_prio_level[irq] = 2; + + /* enable secondary masking method if present */ + if (data[!primary]) + _intc_enable(irq, data[!primary]); + + /* add irq to d->prio list if priority is available */ + if (data[1]) { + hp = d->prio + d->nr_prio; + hp->irq = irq; + hp->handle = data[1]; + + if (primary) { + /* + * only secondary priority should access registers, so + * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() + */ + + hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); + hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); + } + d->nr_prio++; + } + + /* add irq to d->sense list if sense is available */ + data[0] = intc_sense_data(desc, d, enum_id); + if (data[0]) { + (d->sense + d->nr_sense)->irq = irq; + (d->sense + d->nr_sense)->handle = data[0]; + d->nr_sense++; + } + + /* irq should be disabled by default */ + d->chip.mask(irq); + +#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) + if (desc->ack_regs) + ack_handle[irq] = intc_ack_data(desc, d, enum_id); +#endif +} + +static unsigned int __init save_reg(struct intc_desc_int *d, + unsigned int cnt, + unsigned long value, + unsigned int smp) +{ + if (value) { + d->reg[cnt] = value; +#ifdef CONFIG_SMP + d->smp[cnt] = smp; +#endif + return 1; + } + + return 0; +} + + +void __init register_intc_controller(struct intc_desc *desc) +{ + unsigned int i, k, smp; + struct intc_desc_int *d; + + d = alloc_bootmem(sizeof(*d)); + + d->nr_reg = desc->mask_regs ? desc->nr_mask_regs * 2 : 0; + d->nr_reg += desc->prio_regs ? desc->nr_prio_regs * 2 : 0; + d->nr_reg += desc->sense_regs ? desc->nr_sense_regs : 0; + +#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) + d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0; +#endif + d->reg = alloc_bootmem(d->nr_reg * sizeof(*d->reg)); +#ifdef CONFIG_SMP + d->smp = alloc_bootmem(d->nr_reg * sizeof(*d->smp)); +#endif + k = 0; + + if (desc->mask_regs) { + for (i = 0; i < desc->nr_mask_regs; i++) { + smp = IS_SMP(desc->mask_regs[i]); + k += save_reg(d, k, desc->mask_regs[i].set_reg, smp); + k += save_reg(d, k, desc->mask_regs[i].clr_reg, smp); + } + } + + if (desc->prio_regs) { + d->prio = alloc_bootmem(desc->nr_vectors * sizeof(*d->prio)); + + for (i = 0; i < desc->nr_prio_regs; i++) { + smp = IS_SMP(desc->prio_regs[i]); + k += save_reg(d, k, desc->prio_regs[i].set_reg, smp); + k += save_reg(d, k, desc->prio_regs[i].clr_reg, smp); + } + } + + if (desc->sense_regs) { + d->sense = alloc_bootmem(desc->nr_vectors * sizeof(*d->sense)); + + for (i = 0; i < desc->nr_sense_regs; i++) { + k += save_reg(d, k, desc->sense_regs[i].reg, 0); + } + } + + d->chip.name = desc->name; + d->chip.mask = intc_disable; + d->chip.unmask = intc_enable; + d->chip.mask_ack = intc_disable; + d->chip.set_type = intc_set_sense; + +#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) + if (desc->ack_regs) { + for (i = 0; i < desc->nr_ack_regs; i++) + k += save_reg(d, k, desc->ack_regs[i].set_reg, 0); + + d->chip.mask_ack = intc_mask_ack; + } +#endif + + BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ + + for (i = 0; i < desc->nr_vectors; i++) { + struct intc_vect *vect = desc->vectors + i; + + intc_register_irq(desc, d, vect->enum_id, evt2irq(vect->vect)); + } +} diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h new file mode 100644 index 000000000000..68e212ff9dde --- /dev/null +++ b/include/linux/sh_intc.h @@ -0,0 +1,91 @@ +#ifndef __SH_INTC_H +#define __SH_INTC_H + +typedef unsigned char intc_enum; + +struct intc_vect { + intc_enum enum_id; + unsigned short vect; +}; + +#define INTC_VECT(enum_id, vect) { enum_id, vect } +#define INTC_IRQ(enum_id, irq) INTC_VECT(enum_id, irq2evt(irq)) + +struct intc_group { + intc_enum enum_id; + intc_enum enum_ids[32]; +}; + +#define INTC_GROUP(enum_id, ids...) { enum_id, { ids } } + +struct intc_mask_reg { + unsigned long set_reg, clr_reg, reg_width; + intc_enum enum_ids[32]; +#ifdef CONFIG_SMP + unsigned long smp; +#endif +}; + +struct intc_prio_reg { + unsigned long set_reg, clr_reg, reg_width, field_width; + intc_enum enum_ids[16]; +#ifdef CONFIG_SMP + unsigned long smp; +#endif +}; + +struct intc_sense_reg { + unsigned long reg, reg_width, field_width; + intc_enum enum_ids[16]; +}; + +#ifdef CONFIG_SMP +#define INTC_SMP(stride, nr) .smp = (stride) | ((nr) << 8) +#else +#define INTC_SMP(stride, nr) +#endif + +struct intc_desc { + struct intc_vect *vectors; + unsigned int nr_vectors; + struct intc_group *groups; + unsigned int nr_groups; + struct intc_mask_reg *mask_regs; + unsigned int nr_mask_regs; + struct intc_prio_reg *prio_regs; + unsigned int nr_prio_regs; + struct intc_sense_reg *sense_regs; + unsigned int nr_sense_regs; + char *name; +#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) + struct intc_mask_reg *ack_regs; + unsigned int nr_ack_regs; +#endif +}; + +#define _INTC_ARRAY(a) a, sizeof(a)/sizeof(*a) +#define DECLARE_INTC_DESC(symbol, chipname, vectors, groups, \ + mask_regs, prio_regs, sense_regs) \ +struct intc_desc symbol __initdata = { \ + _INTC_ARRAY(vectors), _INTC_ARRAY(groups), \ + _INTC_ARRAY(mask_regs), _INTC_ARRAY(prio_regs), \ + _INTC_ARRAY(sense_regs), \ + chipname, \ +} + +#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A) +#define DECLARE_INTC_DESC_ACK(symbol, chipname, vectors, groups, \ + mask_regs, prio_regs, sense_regs, ack_regs) \ +struct intc_desc symbol __initdata = { \ + _INTC_ARRAY(vectors), _INTC_ARRAY(groups), \ + _INTC_ARRAY(mask_regs), _INTC_ARRAY(prio_regs), \ + _INTC_ARRAY(sense_regs), \ + chipname, \ + _INTC_ARRAY(ack_regs), \ +} +#endif + +void __init register_intc_controller(struct intc_desc *desc); +int intc_set_priority(unsigned int irq, unsigned int prio); + +#endif /* __SH_INTC_H */ -- cgit v1.2.3 From 225c9a8d1da274bf23efec43ec28b1c9e45e12f8 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 1 Oct 2008 16:24:32 +0900 Subject: video: sh_mobile_lcdcfb: Support HAVE_CLK=n configurations. This provides a workaround for users of sh_mobile_lcdcfb that don't define HAVE_CLK and have otherwise sane clock initialization. At the same time, move the sh_mobile_lcdc.h header to include/video/. Signed-off-by: Paul Mundt --- arch/sh/boards/board-ap325rxa.c | 2 +- arch/sh/boards/mach-migor/lcd_qvga.c | 2 +- arch/sh/boards/mach-migor/setup.c | 2 +- arch/sh/include/asm/migor.h | 2 +- arch/sh/include/asm/sh_mobile_lcdc.h | 72 ------------------------------------ drivers/video/sh_mobile_lcdcfb.c | 8 +++- include/video/sh_mobile_lcdc.h | 72 ++++++++++++++++++++++++++++++++++++ 7 files changed, 83 insertions(+), 77 deletions(-) delete mode 100644 arch/sh/include/asm/sh_mobile_lcdc.h create mode 100644 include/video/sh_mobile_lcdc.h (limited to 'arch/sh/include/asm') diff --git a/arch/sh/boards/board-ap325rxa.c b/arch/sh/boards/board-ap325rxa.c index 00e632fc0688..7ae8dcddfeb4 100644 --- a/arch/sh/boards/board-ap325rxa.c +++ b/arch/sh/boards/board-ap325rxa.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include