sh: machine_ops based reboot support.

This provides a machine_ops-based reboot interface loosely cloned from
x86, and converts the native sh32 and sh64 cases over to it.

Necessary both for tying in SMP support and also enabling platforms like
SDK7786 to add support for their microcontroller-based power managers.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
Paul Mundt 2010-01-20 16:42:52 +09:00
parent 2efa53b269
commit fbb82b0365
11 changed files with 156 additions and 90 deletions

View File

@ -0,0 +1,21 @@
#ifndef __ASM_SH_REBOOT_H
#define __ASM_SH_REBOOT_H
#include <linux/kdebug.h>
struct pt_regs;
struct machine_ops {
void (*restart)(char *cmd);
void (*halt)(void);
void (*power_off)(void);
void (*shutdown)(void);
void (*crash_shutdown)(struct pt_regs *);
};
extern struct machine_ops machine_ops;
/* arch/sh/kernel/machine_kexec.c */
void native_machine_crash_shutdown(struct pt_regs *regs);
#endif /* __ASM_SH_REBOOT_H */

View File

@ -143,6 +143,7 @@ extern struct dentry *sh_debugfs_root;
void per_cpu_trap_init(void);
void default_idle(void);
void cpu_idle_wait(void);
void stop_this_cpu(void *);
#ifdef CONFIG_SUPERH32
#define BUILD_TRAP_HANDLER(name) \

View File

@ -2,6 +2,7 @@
#define __ASM_SH_SYSTEM_32_H
#include <linux/types.h>
#include <asm/mmu.h>
#ifdef CONFIG_SH_DSP
@ -216,6 +217,17 @@ static inline reg_size_t register_align(void *val)
int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs,
struct mem_access *ma, int);
static inline void trigger_address_error(void)
{
if (__in_29bit_mode())
__asm__ __volatile__ (
"ldc %0, sr\n\t"
"mov.l @%1, %0"
:
: "r" (0x10000000), "r" (0x80000001)
);
}
asmlinkage void do_address_error(struct pt_regs *regs,
unsigned long writeaccess,
unsigned long address);

View File

@ -48,6 +48,13 @@ static inline reg_size_t register_align(void *val)
return (unsigned long long)(signed long long)(signed long)val;
}
extern void phys_stext(void);
static inline void trigger_address_error(void)
{
phys_stext();
}
#define SR_BL_LL 0x0000000010000000LL
static inline void set_bl_bit(void)

View File

@ -14,7 +14,8 @@ CFLAGS_REMOVE_return_address.o = -pg
obj-y := debugtraps.o dma-nommu.o dumpstack.o \
idle.o io.o io_generic.o irq.o \
irq_$(BITS).o machvec.o nmi_debug.o process.o \
process_$(BITS).o ptrace_$(BITS).o return_address.o \
process_$(BITS).o ptrace_$(BITS).o \
reboot.o return_address.o \
setup.o signal_$(BITS).o sys_sh.o sys_sh$(BITS).o \
syscalls_$(BITS).o time.o topology.o traps.o \
traps_$(BITS).o unwinder.o

View File

@ -20,10 +20,9 @@
#include <asm/system.h>
#include <asm/atomic.h>
static int hlt_counter;
void (*pm_idle)(void) = NULL;
void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off);
static int hlt_counter;
static int __init nohlt_setup(char *__unused)
{
@ -131,6 +130,15 @@ static void do_nothing(void *unused)
{
}
void stop_this_cpu(void *unused)
{
local_irq_disable();
cpu_clear(smp_processor_id(), cpu_online_map);
for (;;)
cpu_sleep();
}
/*
* cpu_idle_wait - Used to ensure that all the CPUs discard old value of
* pm_idle and update to new pm_idle value. Required while changing pm_idle

View File

@ -22,6 +22,7 @@
#include <asm/io.h>
#include <asm/cacheflush.h>
#include <asm/sh_bios.h>
#include <asm/reboot.h>
typedef void (*relocate_new_kernel_t)(unsigned long indirection_page,
unsigned long reboot_code_buffer,
@ -31,12 +32,9 @@ extern const unsigned char relocate_new_kernel[];
extern const unsigned int relocate_new_kernel_size;
extern void *vbr_base;
void machine_shutdown(void)
{
}
void machine_crash_shutdown(struct pt_regs *regs)
void native_machine_crash_shutdown(struct pt_regs *regs)
{
/* Nothing to do for UP, but definitely broken for SMP.. */
}
/*

View File

@ -16,63 +16,15 @@
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/elfcore.h>
#include <linux/pm.h>
#include <linux/kallsyms.h>
#include <linux/kexec.h>
#include <linux/kdebug.h>
#include <linux/tick.h>
#include <linux/reboot.h>
#include <linux/fs.h>
#include <linux/ftrace.h>
#include <linux/preempt.h>
#include <linux/hw_breakpoint.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
#include <asm/pgalloc.h>
#include <asm/system.h>
#include <asm/fpu.h>
#include <asm/syscalls.h>
#include <asm/watchdog.h>
#ifdef CONFIG_32BIT
static void watchdog_trigger_immediate(void)
{
sh_wdt_write_cnt(0xFF);
sh_wdt_write_csr(0xC2);
}
void machine_restart(char * __unused)
{
local_irq_disable();
/* Use watchdog timer to trigger reset */
watchdog_trigger_immediate();
while (1)
cpu_sleep();
}
#else
void machine_restart(char * __unused)
{
/* SR.BL=1 and invoke address error to let CPU reset (manual reset) */
asm volatile("ldc %0, sr\n\t"
"mov.l @%1, %0" : : "r" (0x10000000), "r" (0x80000001));
}
#endif
void machine_halt(void)
{
local_irq_disable();
while (1)
cpu_sleep();
}
void machine_power_off(void)
{
if (pm_power_off)
pm_power_off();
}
void show_regs(struct pt_regs * regs)
{

View File

@ -32,30 +32,7 @@
struct task_struct *last_task_used_math = NULL;
void machine_restart(char * __unused)
{
extern void phys_stext(void);
phys_stext();
}
void machine_halt(void)
{
for (;;);
}
void machine_power_off(void)
{
__asm__ __volatile__ (
"sleep\n\t"
"synci\n\t"
"nop;nop;nop;nop\n\t"
);
panic("Unexpected wakeup!\n");
}
void show_regs(struct pt_regs * regs)
void show_regs(struct pt_regs *regs)
{
unsigned long long ah, al, bh, bl, ch, cl;

98
arch/sh/kernel/reboot.c Normal file
View File

@ -0,0 +1,98 @@
#include <linux/pm.h>
#include <linux/kexec.h>
#include <linux/kernel.h>
#include <linux/reboot.h>
#include <linux/module.h>
#ifdef CONFIG_SUPERH32
#include <asm/watchdog.h>
#endif
#include <asm/addrspace.h>
#include <asm/reboot.h>
#include <asm/system.h>
void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off);
#ifdef CONFIG_SUPERH32
static void watchdog_trigger_immediate(void)
{
sh_wdt_write_cnt(0xFF);
sh_wdt_write_csr(0xC2);
}
#endif
static void native_machine_restart(char * __unused)
{
local_irq_disable();
/* Address error with SR.BL=1 first. */
trigger_address_error();
#ifdef CONFIG_SUPERH32
/* If that fails or is unsupported, go for the watchdog next. */
watchdog_trigger_immediate();
#endif
/*
* Give up and sleep.
*/
while (1)
cpu_sleep();
}
static void native_machine_shutdown(void)
{
smp_send_stop();
}
static void native_machine_power_off(void)
{
if (pm_power_off)
pm_power_off();
}
static void native_machine_halt(void)
{
/* stop other cpus */
machine_shutdown();
/* stop this cpu */
stop_this_cpu(NULL);
}
struct machine_ops machine_ops = {
.power_off = native_machine_power_off,
.shutdown = native_machine_shutdown,
.restart = native_machine_restart,
.halt = native_machine_halt,
#ifdef CONFIG_KEXEC
.crash_shutdown = native_machine_crash_shutdown,
#endif
};
void machine_power_off(void)
{
machine_ops.power_off();
}
void machine_shutdown(void)
{
machine_ops.shutdown();
}
void machine_restart(char *cmd)
{
machine_ops.restart(cmd);
}
void machine_halt(void)
{
machine_ops.halt();
}
#ifdef CONFIG_KEXEC
void machine_crash_shutdown(struct pt_regs *regs)
{
machine_ops.crash_shutdown(regs);
}
#endif

View File

@ -161,15 +161,6 @@ void smp_send_reschedule(int cpu)
plat_send_ipi(cpu, SMP_MSG_RESCHEDULE);
}
static void stop_this_cpu(void *unused)
{
cpu_clear(smp_processor_id(), cpu_online_map);
local_irq_disable();
for (;;)
cpu_relax();
}
void smp_send_stop(void)
{
smp_call_function(stop_this_cpu, 0, 0);