Linux-libre 3.5.4-gnu1
[librecmc/linux-libre.git] / arch / c6x / kernel / process.c
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
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) {
58                 tick_nohz_idle_enter();
59                 rcu_idle_enter();
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                 }
68                 rcu_idle_exit();
69                 tick_nohz_idle_exit();
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(&regs, 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, &regs,
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 }