|  | /* | 
|  | *  Port on Texas Instruments TMS320C6x architecture | 
|  | * | 
|  | *  Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated | 
|  | *  Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | 
|  | * | 
|  | *  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); | 
|  |  | 
|  | /* | 
|  | * 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; | 
|  | } |