| Aurelien Jacquiot | 687b12b | 2011-10-04 11:03:44 -0400 | [diff] [blame] | 1 | /* | 
 | 2 |  *  Port on Texas Instruments TMS320C6x architecture | 
 | 3 |  * | 
 | 4 |  *  Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated | 
 | 5 |  *  Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | 
 | 6 |  * | 
 | 7 |  *  This program is free software; you can redistribute it and/or modify | 
 | 8 |  *  it under the terms of the GNU General Public License version 2 as | 
 | 9 |  *  published by the Free Software Foundation. | 
 | 10 |  * | 
 | 11 |  */ | 
 | 12 | #include <linux/module.h> | 
 | 13 | #include <linux/unistd.h> | 
 | 14 | #include <linux/ptrace.h> | 
 | 15 | #include <linux/init_task.h> | 
 | 16 | #include <linux/tick.h> | 
 | 17 | #include <linux/mqueue.h> | 
 | 18 | #include <linux/syscalls.h> | 
 | 19 | #include <linux/reboot.h> | 
 | 20 |  | 
 | 21 | #include <asm/syscalls.h> | 
 | 22 |  | 
 | 23 | /* hooks for board specific support */ | 
 | 24 | void	(*c6x_restart)(void); | 
 | 25 | void	(*c6x_halt)(void); | 
 | 26 |  | 
 | 27 | extern asmlinkage void ret_from_fork(void); | 
 | 28 |  | 
| Aurelien Jacquiot | 687b12b | 2011-10-04 11:03:44 -0400 | [diff] [blame] | 29 | /* | 
 | 30 |  * power off function, if any | 
 | 31 |  */ | 
 | 32 | void (*pm_power_off)(void); | 
 | 33 | EXPORT_SYMBOL(pm_power_off); | 
 | 34 |  | 
 | 35 | static void c6x_idle(void) | 
 | 36 | { | 
 | 37 | 	unsigned long tmp; | 
 | 38 |  | 
 | 39 | 	/* | 
 | 40 | 	 * Put local_irq_enable and idle in same execute packet | 
 | 41 | 	 * to make them atomic and avoid race to idle with | 
 | 42 | 	 * interrupts enabled. | 
 | 43 | 	 */ | 
 | 44 | 	asm volatile ("   mvc .s2 CSR,%0\n" | 
 | 45 | 		      "   or  .d2 1,%0,%0\n" | 
 | 46 | 		      "   mvc .s2 %0,CSR\n" | 
 | 47 | 		      "|| idle\n" | 
 | 48 | 		      : "=b"(tmp)); | 
 | 49 | } | 
 | 50 |  | 
 | 51 | /* | 
 | 52 |  * The idle loop for C64x | 
 | 53 |  */ | 
 | 54 | void cpu_idle(void) | 
 | 55 | { | 
 | 56 | 	/* endless idle loop with no priority at all */ | 
 | 57 | 	while (1) { | 
| Mark Salter | 166c0ea | 2012-01-08 13:25:56 -0500 | [diff] [blame] | 58 | 		tick_nohz_idle_enter(); | 
 | 59 | 		rcu_idle_enter(); | 
| Aurelien Jacquiot | 687b12b | 2011-10-04 11:03:44 -0400 | [diff] [blame] | 60 | 		while (1) { | 
 | 61 | 			local_irq_disable(); | 
 | 62 | 			if (need_resched()) { | 
 | 63 | 				local_irq_enable(); | 
 | 64 | 				break; | 
 | 65 | 			} | 
 | 66 | 			c6x_idle(); /* enables local irqs */ | 
 | 67 | 		} | 
| Mark Salter | 166c0ea | 2012-01-08 13:25:56 -0500 | [diff] [blame] | 68 | 		rcu_idle_exit(); | 
 | 69 | 		tick_nohz_idle_exit(); | 
| Aurelien Jacquiot | 687b12b | 2011-10-04 11:03:44 -0400 | [diff] [blame] | 70 |  | 
 | 71 | 		preempt_enable_no_resched(); | 
 | 72 | 		schedule(); | 
 | 73 | 		preempt_disable(); | 
 | 74 | 	} | 
 | 75 | } | 
 | 76 |  | 
 | 77 | static void halt_loop(void) | 
 | 78 | { | 
 | 79 | 	printk(KERN_EMERG "System Halted, OK to turn off power\n"); | 
 | 80 | 	local_irq_disable(); | 
 | 81 | 	while (1) | 
 | 82 | 		asm volatile("idle\n"); | 
 | 83 | } | 
 | 84 |  | 
 | 85 | void machine_restart(char *__unused) | 
 | 86 | { | 
 | 87 | 	if (c6x_restart) | 
 | 88 | 		c6x_restart(); | 
 | 89 | 	halt_loop(); | 
 | 90 | } | 
 | 91 |  | 
 | 92 | void machine_halt(void) | 
 | 93 | { | 
 | 94 | 	if (c6x_halt) | 
 | 95 | 		c6x_halt(); | 
 | 96 | 	halt_loop(); | 
 | 97 | } | 
 | 98 |  | 
 | 99 | void machine_power_off(void) | 
 | 100 | { | 
 | 101 | 	if (pm_power_off) | 
 | 102 | 		pm_power_off(); | 
 | 103 | 	halt_loop(); | 
 | 104 | } | 
 | 105 |  | 
 | 106 | static void kernel_thread_helper(int dummy, void *arg, int (*fn)(void *)) | 
 | 107 | { | 
 | 108 | 	do_exit(fn(arg)); | 
 | 109 | } | 
 | 110 |  | 
 | 111 | /* | 
 | 112 |  * Create a kernel thread | 
 | 113 |  */ | 
 | 114 | int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) | 
 | 115 | { | 
 | 116 | 	struct pt_regs regs; | 
 | 117 |  | 
 | 118 | 	/* | 
 | 119 | 	 * copy_thread sets a4 to zero (child return from fork) | 
 | 120 | 	 * so we can't just set things up to directly return to | 
 | 121 | 	 * fn. | 
 | 122 | 	 */ | 
 | 123 | 	memset(®s, 0, sizeof(regs)); | 
 | 124 | 	regs.b4 = (unsigned long) arg; | 
 | 125 | 	regs.a6 = (unsigned long) fn; | 
 | 126 | 	regs.pc = (unsigned long) kernel_thread_helper; | 
 | 127 | 	local_save_flags(regs.csr); | 
 | 128 | 	regs.csr |= 1; | 
 | 129 | 	regs.tsr = 5; /* Set GEE and GIE in TSR */ | 
 | 130 |  | 
 | 131 | 	/* Ok, create the new process.. */ | 
 | 132 | 	return do_fork(flags | CLONE_VM | CLONE_UNTRACED, -1, ®s, | 
 | 133 | 		       0, NULL, NULL); | 
 | 134 | } | 
 | 135 | EXPORT_SYMBOL(kernel_thread); | 
 | 136 |  | 
 | 137 | void flush_thread(void) | 
 | 138 | { | 
 | 139 | } | 
 | 140 |  | 
 | 141 | void exit_thread(void) | 
 | 142 | { | 
 | 143 | } | 
 | 144 |  | 
 | 145 | SYSCALL_DEFINE1(c6x_clone, struct pt_regs *, regs) | 
 | 146 | { | 
 | 147 | 	unsigned long clone_flags; | 
 | 148 | 	unsigned long newsp; | 
 | 149 |  | 
 | 150 | 	/* syscall puts clone_flags in A4 and usp in B4 */ | 
 | 151 | 	clone_flags = regs->orig_a4; | 
 | 152 | 	if (regs->b4) | 
 | 153 | 		newsp = regs->b4; | 
 | 154 | 	else | 
 | 155 | 		newsp = regs->sp; | 
 | 156 |  | 
 | 157 | 	return do_fork(clone_flags, newsp, regs, 0, (int __user *)regs->a6, | 
 | 158 | 		       (int __user *)regs->b6); | 
 | 159 | } | 
 | 160 |  | 
 | 161 | /* | 
 | 162 |  * Do necessary setup to start up a newly executed thread. | 
 | 163 |  */ | 
 | 164 | void start_thread(struct pt_regs *regs, unsigned int pc, unsigned long usp) | 
 | 165 | { | 
 | 166 | 	/* | 
 | 167 | 	 * The binfmt loader will setup a "full" stack, but the C6X | 
 | 168 | 	 * operates an "empty" stack. So we adjust the usp so that | 
 | 169 | 	 * argc doesn't get destroyed if an interrupt is taken before | 
 | 170 | 	 * it is read from the stack. | 
 | 171 | 	 * | 
 | 172 | 	 * NB: Library startup code needs to match this. | 
 | 173 | 	 */ | 
 | 174 | 	usp -= 8; | 
 | 175 |  | 
 | 176 | 	set_fs(USER_DS); | 
 | 177 | 	regs->pc  = pc; | 
 | 178 | 	regs->sp  = usp; | 
 | 179 | 	regs->tsr |= 0x40; /* set user mode */ | 
 | 180 | 	current->thread.usp = usp; | 
 | 181 | } | 
 | 182 |  | 
 | 183 | /* | 
 | 184 |  * Copy a new thread context in its stack. | 
 | 185 |  */ | 
 | 186 | int copy_thread(unsigned long clone_flags, unsigned long usp, | 
 | 187 | 		unsigned long ustk_size, | 
 | 188 | 		struct task_struct *p, struct pt_regs *regs) | 
 | 189 | { | 
 | 190 | 	struct pt_regs *childregs; | 
 | 191 |  | 
 | 192 | 	childregs = task_pt_regs(p); | 
 | 193 |  | 
 | 194 | 	*childregs = *regs; | 
 | 195 | 	childregs->a4 = 0; | 
 | 196 |  | 
 | 197 | 	if (usp == -1) | 
 | 198 | 		/* case of  __kernel_thread: we return to supervisor space */ | 
 | 199 | 		childregs->sp = (unsigned long)(childregs + 1); | 
 | 200 | 	else | 
 | 201 | 		/* Otherwise use the given stack */ | 
 | 202 | 		childregs->sp = usp; | 
 | 203 |  | 
 | 204 | 	/* Set usp/ksp */ | 
 | 205 | 	p->thread.usp = childregs->sp; | 
 | 206 | 	/* switch_to uses stack to save/restore 14 callee-saved regs */ | 
 | 207 | 	thread_saved_ksp(p) = (unsigned long)childregs - 8; | 
 | 208 | 	p->thread.pc = (unsigned int) ret_from_fork; | 
 | 209 | 	p->thread.wchan	= (unsigned long) ret_from_fork; | 
 | 210 | #ifdef __DSBT__ | 
 | 211 | 	{ | 
 | 212 | 		unsigned long dp; | 
 | 213 |  | 
 | 214 | 		asm volatile ("mv .S2 b14,%0\n" : "=b"(dp)); | 
 | 215 |  | 
 | 216 | 		thread_saved_dp(p) = dp; | 
 | 217 | 		if (usp == -1) | 
 | 218 | 			childregs->dp = dp; | 
 | 219 | 	} | 
 | 220 | #endif | 
 | 221 | 	return 0; | 
 | 222 | } | 
 | 223 |  | 
 | 224 | /* | 
 | 225 |  * c6x_execve() executes a new program. | 
 | 226 |  */ | 
 | 227 | SYSCALL_DEFINE4(c6x_execve, const char __user *, name, | 
 | 228 | 		const char __user *const __user *, argv, | 
 | 229 | 		const char __user *const __user *, envp, | 
 | 230 | 		struct pt_regs *, regs) | 
 | 231 | { | 
 | 232 | 	int error; | 
 | 233 | 	char *filename; | 
 | 234 |  | 
 | 235 | 	filename = getname(name); | 
 | 236 | 	error = PTR_ERR(filename); | 
 | 237 | 	if (IS_ERR(filename)) | 
 | 238 | 		goto out; | 
 | 239 |  | 
 | 240 | 	error = do_execve(filename, argv, envp, regs); | 
 | 241 | 	putname(filename); | 
 | 242 | out: | 
 | 243 | 	return error; | 
 | 244 | } | 
 | 245 |  | 
 | 246 | unsigned long get_wchan(struct task_struct *p) | 
 | 247 | { | 
 | 248 | 	return p->thread.wchan; | 
 | 249 | } |