| 
									
										
										
										
											2011-10-04 11:03:44 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  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); | 
					
						
							| 
									
										
										
										
											2012-09-21 12:26:37 -04:00
										 |  |  | extern asmlinkage void ret_from_kernel_thread(void); | 
					
						
							| 
									
										
										
										
											2011-10-04 11:03:44 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * power off function, if any | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void (*pm_power_off)(void); | 
					
						
							|  |  |  | EXPORT_SYMBOL(pm_power_off); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-21 22:49:42 +01:00
										 |  |  | void arch_cpu_idle(void) | 
					
						
							| 
									
										
										
										
											2011-10-04 11:03:44 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	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)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void flush_thread(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void exit_thread(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * 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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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, | 
					
						
							| 
									
										
										
										
											2012-10-22 22:51:14 -04:00
										 |  |  | 		struct task_struct *p) | 
					
						
							| 
									
										
										
										
											2011-10-04 11:03:44 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct pt_regs *childregs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	childregs = task_pt_regs(p); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-21 16:37:49 -04:00
										 |  |  | 	if (unlikely(p->flags & PF_KTHREAD)) { | 
					
						
							| 
									
										
										
										
											2011-10-04 11:03:44 -04:00
										 |  |  | 		/* case of  __kernel_thread: we return to supervisor space */ | 
					
						
							| 
									
										
										
										
											2012-09-22 18:23:49 -04:00
										 |  |  | 		memset(childregs, 0, sizeof(struct pt_regs)); | 
					
						
							| 
									
										
										
										
											2011-10-04 11:03:44 -04:00
										 |  |  | 		childregs->sp = (unsigned long)(childregs + 1); | 
					
						
							| 
									
										
										
										
											2012-09-21 12:26:37 -04:00
										 |  |  | 		p->thread.pc = (unsigned long) ret_from_kernel_thread; | 
					
						
							| 
									
										
										
										
											2012-09-22 18:23:49 -04:00
										 |  |  | 		childregs->a0 = usp;		/* function */ | 
					
						
							|  |  |  | 		childregs->a1 = ustk_size;	/* argument */ | 
					
						
							| 
									
										
										
										
											2012-09-21 12:26:37 -04:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2011-10-04 11:03:44 -04:00
										 |  |  | 		/* Otherwise use the given stack */ | 
					
						
							| 
									
										
										
										
											2012-10-21 16:37:49 -04:00
										 |  |  | 		*childregs = *current_pt_regs(); | 
					
						
							|  |  |  | 		if (usp) | 
					
						
							|  |  |  | 			childregs->sp = usp; | 
					
						
							| 
									
										
										
										
											2012-09-21 12:26:37 -04:00
										 |  |  | 		p->thread.pc = (unsigned long) ret_from_fork; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-10-04 11:03:44 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Set usp/ksp */ | 
					
						
							|  |  |  | 	p->thread.usp = childregs->sp; | 
					
						
							|  |  |  | 	thread_saved_ksp(p) = (unsigned long)childregs - 8; | 
					
						
							| 
									
										
										
										
											2012-09-21 12:26:37 -04:00
										 |  |  | 	p->thread.wchan	= p->thread.pc; | 
					
						
							| 
									
										
										
										
											2011-10-04 11:03:44 -04:00
										 |  |  | #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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | unsigned long get_wchan(struct task_struct *p) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return p->thread.wchan; | 
					
						
							|  |  |  | } |