diff options
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/armksyms.c | 4 | ||||
-rw-r--r-- | arch/arm/kernel/elf.c | 6 | ||||
-rw-r--r-- | arch/arm/kernel/hw_breakpoint.c | 25 | ||||
-rw-r--r-- | arch/arm/kernel/machine_kexec.c | 13 | ||||
-rw-r--r-- | arch/arm/kernel/process.c | 11 | ||||
-rw-r--r-- | arch/arm/kernel/return_address.c | 4 | ||||
-rw-r--r-- | arch/arm/kernel/smp.c | 4 | ||||
-rw-r--r-- | arch/arm/kernel/smp_twd.c | 30 |
8 files changed, 82 insertions, 15 deletions
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index 5b0bce61eb6..f0c41295216 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -49,10 +49,6 @@ extern void __aeabi_ulcmp(void); extern void fpundefinstr(void); - /* platform dependent support */ -EXPORT_SYMBOL(__udelay); -EXPORT_SYMBOL(__const_udelay); - /* networking */ EXPORT_SYMBOL(csum_partial); EXPORT_SYMBOL(csum_partial_copy_from_user); diff --git a/arch/arm/kernel/elf.c b/arch/arm/kernel/elf.c index ddba41d1fcf..cac241a8415 100644 --- a/arch/arm/kernel/elf.c +++ b/arch/arm/kernel/elf.c @@ -4,11 +4,13 @@ #include <linux/binfmts.h> #include <linux/elf.h> -int elf_check_arch(const struct elf32_hdr *x) +int arm_elf_check_arch(const struct elf32_hdr *x) { unsigned int eflags; /* Make sure it's an ARM executable */ + if (x->e_ident[EI_CLASS] != ELF_CLASS) + return 0; if (x->e_machine != EM_ARM) return 0; @@ -35,7 +37,7 @@ int elf_check_arch(const struct elf32_hdr *x) } return 1; } -EXPORT_SYMBOL(elf_check_arch); +EXPORT_SYMBOL(arm_elf_check_arch); void elf_set_personality(const struct elf32_hdr *x) { diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index d6a95ef9131..39fb65fda43 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -854,6 +854,25 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, return ret; } +static int hw_breakpoint_undef(struct pt_regs *regs, unsigned int instr) +{ + int reg = (instr >> 12) & 15; + + /* Fake sticky power-down cleared */ + regs->uregs[reg] = 0; + regs->ARM_pc += 4; + + return 0; +} + +static struct undef_hook hw_breakpoint_hook = { + .instr_mask = 0xffff0fff, + .instr_val = 0xee110e95, + .cpsr_mask = MODE_MASK, + .cpsr_val = SVC_MODE, + .fn = hw_breakpoint_undef, +}; + /* * One-time initialisation. */ @@ -900,6 +919,10 @@ static void reset_ctrl_regs(void *unused) /* * Ensure sticky power-down is clear (i.e. debug logic is * powered up). + * + * This could raise an undefined instruction exception. If it + * does, it is fixed up with an undef hook which constructs + * a fake value with the sticky power-down bit cleared. */ asm volatile("mrc p14, 0, %0, c1, c5, 4" : "=r" (dbg_power)); if ((dbg_power & 0x1) == 0) @@ -987,6 +1010,8 @@ static int __init arch_hw_breakpoint_init(void) */ register_undef_hook(&debug_reg_hook); + register_undef_hook(&hw_breakpoint_hook); + /* * Reset the breakpoint resources. We assume that a halting * debugger will leave the world in a nice state for us. diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index 764bd456d84..d76767f6509 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -46,6 +46,7 @@ void machine_crash_nonpanic_core(void *unused) printk(KERN_DEBUG "CPU %u will stop doing anything useful since another CPU has crashed\n", smp_processor_id()); crash_save_cpu(®s, smp_processor_id()); + atomic_notifier_call_chain(&crash_percpu_notifier_list, 0, NULL); flush_cache_all(); atomic_dec(&waiting_for_crash_ipi); @@ -59,6 +60,8 @@ void machine_crash_shutdown(struct pt_regs *regs) local_irq_disable(); + atomic_notifier_call_chain(&crash_percpu_notifier_list, 0, NULL); + atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); smp_call_function(machine_crash_nonpanic_core, NULL, false); msecs = 1000; /* Wait at most a second for the other cpus to stop */ @@ -113,3 +116,13 @@ void machine_kexec(struct kimage *image) soft_restart(reboot_code_buffer_phys); } + +void machine_crash_swreset(void) +{ + printk(KERN_INFO "Software reset on panic!\n"); + + flush_cache_all(); + outer_flush_all(); + outer_disable(); + arm_pm_restart(0, NULL); +} diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 971d65c253a..14b20051939 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -211,8 +211,17 @@ void cpu_idle(void) leds_event(led_idle_start); while (!need_resched()) { #ifdef CONFIG_HOTPLUG_CPU - if (cpu_is_offline(smp_processor_id())) + if (cpu_is_offline(smp_processor_id())) { + + /* NOTE : preempt_count() should be 0 for dying CPU + * as the CPU will use this very thread when + * it is alive + */ + if (preempt_count()) + preempt_enable_no_resched(); + cpu_die(); + } #endif local_irq_disable(); diff --git a/arch/arm/kernel/return_address.c b/arch/arm/kernel/return_address.c index 8085417555d..0697db65efa 100644 --- a/arch/arm/kernel/return_address.c +++ b/arch/arm/kernel/return_address.c @@ -58,10 +58,6 @@ void *return_address(unsigned int level) #else /* if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) */ -#if defined(CONFIG_ARM_UNWIND) -#warning "TODO: return_address should use unwind tables" -#endif - void *return_address(unsigned int level) { return NULL; diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index cdeb727527d..712b1681f48 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -279,8 +279,6 @@ asmlinkage void __cpuinit secondary_start_kernel(void) notify_cpu_starting(cpu); - calibrate_delay(); - smp_store_cpu_info(cpu); /* @@ -433,7 +431,7 @@ static void ipi_timer(void) } #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST -static void smp_timer_broadcast(const struct cpumask *mask) +void smp_timer_broadcast(const struct cpumask *mask) { smp_cross_call(mask, IPI_TIMER); } diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 7a79b24597b..007f7f6aee8 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -31,6 +31,9 @@ void __iomem *twd_base; static struct clk *twd_clk; static unsigned long twd_timer_rate; +static DEFINE_PER_CPU(u32, twd_ctrl); +static DEFINE_PER_CPU(u32, twd_load); + static struct clock_event_device __percpu **twd_evt; static void twd_set_mode(enum clock_event_mode mode, @@ -115,10 +118,14 @@ static int twd_cpufreq_transition(struct notifier_block *nb, * The twd clock events must be reprogrammed to account for the new * frequency. The timer is local to a cpu, so cross-call to the * changing cpu. + * + * Only wait for it to finish, if the cpu is active to avoid + * deadlock when cpu1 is spinning on while(!cpu_active(cpu1)) during + * booting of that cpu. */ if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) smp_call_function_single(freqs->cpu, twd_update_frequency, - NULL, 1); + NULL, cpu_active(freqs->cpu)); return NOTIFY_OK; } @@ -268,3 +275,24 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) 0xf, 0xffffffff); enable_percpu_irq(clk->irq, 0); } + +#if defined(CONFIG_HOTPLUG) || defined(CONFIG_CPU_IDLE) +void twd_save(void) +{ + int this_cpu = smp_processor_id(); + + per_cpu(twd_ctrl, this_cpu) = __raw_readl(twd_base + TWD_TIMER_CONTROL); + per_cpu(twd_load, this_cpu) = __raw_readl(twd_base + TWD_TIMER_LOAD); + +} + +void twd_restore(void) +{ + int this_cpu = smp_processor_id(); + + __raw_writel(per_cpu(twd_ctrl, this_cpu), + twd_base + TWD_TIMER_CONTROL); + __raw_writel(per_cpu(twd_load, this_cpu), + twd_base + TWD_TIMER_LOAD); +} +#endif |