diff options
Diffstat (limited to 'arch/c6x/kernel/process.c')
| -rw-r--r-- | arch/c6x/kernel/process.c | 265 | 
1 files changed, 265 insertions, 0 deletions
diff --git a/arch/c6x/kernel/process.c b/arch/c6x/kernel/process.c new file mode 100644 index 000000000000..7ca8c41b03cd --- /dev/null +++ b/arch/c6x/kernel/process.c @@ -0,0 +1,265 @@ +/* + *  Port on Texas Instruments TMS320C6x architecture + * + *  Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated + *  Author: Aurelien Jacquiot ([email protected]) + * + *  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 <linux/module.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/init_task.h> +#include <linux/tick.h> +#include <linux/mqueue.h> +#include <linux/syscalls.h> +#include <linux/reboot.h> + +#include <asm/syscalls.h> + +/* hooks for board specific support */ +void	(*c6x_restart)(void); +void	(*c6x_halt)(void); + +extern asmlinkage void ret_from_fork(void); + +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); + +/* + * Initial thread structure. + */ +union thread_union init_thread_union __init_task_data =	{ +	INIT_THREAD_INFO(init_task) +}; + +/* + * Initial task structure. + */ +struct task_struct init_task = INIT_TASK(init_task); +EXPORT_SYMBOL(init_task); + +/* + * power off function, if any + */ +void (*pm_power_off)(void); +EXPORT_SYMBOL(pm_power_off); + +static void c6x_idle(void) +{ +	unsigned long tmp; + +	/* +	 * Put local_irq_enable and idle in same execute packet +	 * to make them atomic and avoid race to idle with +	 * interrupts enabled. +	 */ +	asm volatile ("   mvc .s2 CSR,%0\n" +		      "   or  .d2 1,%0,%0\n" +		      "   mvc .s2 %0,CSR\n" +		      "|| idle\n" +		      : "=b"(tmp)); +} + +/* + * The idle loop for C64x + */ +void cpu_idle(void) +{ +	/* endless idle loop with no priority at all */ +	while (1) { +		tick_nohz_idle_enter(); +		rcu_idle_enter(); +		while (1) { +			local_irq_disable(); +			if (need_resched()) { +				local_irq_enable(); +				break; +			} +			c6x_idle(); /* enables local irqs */ +		} +		rcu_idle_exit(); +		tick_nohz_idle_exit(); + +		preempt_enable_no_resched(); +		schedule(); +		preempt_disable(); +	} +} + +static void halt_loop(void) +{ +	printk(KERN_EMERG "System Halted, OK to turn off power\n"); +	local_irq_disable(); +	while (1) +		asm volatile("idle\n"); +} + +void machine_restart(char *__unused) +{ +	if (c6x_restart) +		c6x_restart(); +	halt_loop(); +} + +void machine_halt(void) +{ +	if (c6x_halt) +		c6x_halt(); +	halt_loop(); +} + +void machine_power_off(void) +{ +	if (pm_power_off) +		pm_power_off(); +	halt_loop(); +} + +static void kernel_thread_helper(int dummy, void *arg, int (*fn)(void *)) +{ +	do_exit(fn(arg)); +} + +/* + * Create a kernel thread + */ +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ +	struct pt_regs regs; + +	/* +	 * copy_thread sets a4 to zero (child return from fork) +	 * so we can't just set things up to directly return to +	 * fn. +	 */ +	memset(®s, 0, sizeof(regs)); +	regs.b4 = (unsigned long) arg; +	regs.a6 = (unsigned long) fn; +	regs.pc = (unsigned long) kernel_thread_helper; +	local_save_flags(regs.csr); +	regs.csr |= 1; +	regs.tsr = 5; /* Set GEE and GIE in TSR */ + +	/* Ok, create the new process.. */ +	return do_fork(flags | CLONE_VM | CLONE_UNTRACED, -1, ®s, +		       0, NULL, NULL); +} +EXPORT_SYMBOL(kernel_thread); + +void flush_thread(void) +{ +} + +void exit_thread(void) +{ +} + +SYSCALL_DEFINE1(c6x_clone, struct pt_regs *, regs) +{ +	unsigned long clone_flags; +	unsigned long newsp; + +	/* syscall puts clone_flags in A4 and usp in B4 */ +	clone_flags = regs->orig_a4; +	if (regs->b4) +		newsp = regs->b4; +	else +		newsp = regs->sp; + +	return do_fork(clone_flags, newsp, regs, 0, (int __user *)regs->a6, +		       (int __user *)regs->b6); +} + +/* + * Do necessary setup to start up a newly executed thread. + */ +void start_thread(struct pt_regs *regs, unsigned int pc, unsigned long usp) +{ +	/* +	 * The binfmt loader will setup a "full" stack, but the C6X +	 * operates an "empty" stack. So we adjust the usp so that +	 * argc doesn't get destroyed if an interrupt is taken before +	 * it is read from the stack. +	 * +	 * NB: Library startup code needs to match this. +	 */ +	usp -= 8; + +	set_fs(USER_DS); +	regs->pc  = pc; +	regs->sp  = usp; +	regs->tsr |= 0x40; /* set user mode */ +	current->thread.usp = usp; +} + +/* + * Copy a new thread context in its stack. + */ +int copy_thread(unsigned long clone_flags, unsigned long usp, +		unsigned long ustk_size, +		struct task_struct *p, struct pt_regs *regs) +{ +	struct pt_regs *childregs; + +	childregs = task_pt_regs(p); + +	*childregs = *regs; +	childregs->a4 = 0; + +	if (usp == -1) +		/* case of  __kernel_thread: we return to supervisor space */ +		childregs->sp = (unsigned long)(childregs + 1); +	else +		/* Otherwise use the given stack */ +		childregs->sp = usp; + +	/* Set usp/ksp */ +	p->thread.usp = childregs->sp; +	/* switch_to uses stack to save/restore 14 callee-saved regs */ +	thread_saved_ksp(p) = (unsigned long)childregs - 8; +	p->thread.pc = (unsigned int) ret_from_fork; +	p->thread.wchan	= (unsigned long) ret_from_fork; +#ifdef __DSBT__ +	{ +		unsigned long dp; + +		asm volatile ("mv .S2 b14,%0\n" : "=b"(dp)); + +		thread_saved_dp(p) = dp; +		if (usp == -1) +			childregs->dp = dp; +	} +#endif +	return 0; +} + +/* + * c6x_execve() executes a new program. + */ +SYSCALL_DEFINE4(c6x_execve, const char __user *, name, +		const char __user *const __user *, argv, +		const char __user *const __user *, envp, +		struct pt_regs *, regs) +{ +	int error; +	char *filename; + +	filename = getname(name); +	error = PTR_ERR(filename); +	if (IS_ERR(filename)) +		goto out; + +	error = do_execve(filename, argv, envp, regs); +	putname(filename); +out: +	return error; +} + +unsigned long get_wchan(struct task_struct *p) +{ +	return p->thread.wchan; +}  |