aboutsummaryrefslogtreecommitdiffstats
path: root/arch/blackfin/kernel/process.c
diff options
context:
space:
mode:
authorBryan Wu <bryan.wu@analog.com>2007-05-06 17:50:22 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-07 15:12:58 -0400
commit1394f03221790a988afc3e4b3cb79f2e477246a9 (patch)
tree2c1963c9a4f2d84a5e021307fde240c5d567cf70 /arch/blackfin/kernel/process.c
parent73243284463a761e04d69d22c7516b2be7de096c (diff)
blackfin architecture
This adds support for the Analog Devices Blackfin processor architecture, and currently supports the BF533, BF532, BF531, BF537, BF536, BF534, and BF561 (Dual Core) devices, with a variety of development platforms including those avaliable from Analog Devices (BF533-EZKit, BF533-STAMP, BF537-STAMP, BF561-EZKIT), and Bluetechnix! Tinyboards. The Blackfin architecture was jointly developed by Intel and Analog Devices Inc. (ADI) as the Micro Signal Architecture (MSA) core and introduced it in December of 2000. Since then ADI has put this core into its Blackfin processor family of devices. The Blackfin core has the advantages of a clean, orthogonal,RISC-like microprocessor instruction set. It combines a dual-MAC (Multiply/Accumulate), state-of-the-art signal processing engine and single-instruction, multiple-data (SIMD) multimedia capabilities into a single instruction-set architecture. The Blackfin architecture, including the instruction set, is described by the ADSP-BF53x/BF56x Blackfin Processor Programming Reference http://blackfin.uclinux.org/gf/download/frsrelease/29/2549/Blackfin_PRM.pdf The Blackfin processor is already supported by major releases of gcc, and there are binary and source rpms/tarballs for many architectures at: http://blackfin.uclinux.org/gf/project/toolchain/frs There is complete documentation, including "getting started" guides available at: http://docs.blackfin.uclinux.org/ which provides links to the sources and patches you will need in order to set up a cross-compiling environment for bfin-linux-uclibc This patch, as well as the other patches (toolchain, distribution, uClibc) are actively supported by Analog Devices Inc, at: http://blackfin.uclinux.org/ We have tested this on LTP, and our test plan (including pass/fails) can be found at: http://docs.blackfin.uclinux.org/doku.php?id=testing_the_linux_kernel [m.kozlowski@tuxland.pl: balance parenthesis in blackfin header files] Signed-off-by: Bryan Wu <bryan.wu@analog.com> Signed-off-by: Mariusz Kozlowski <m.kozlowski@tuxland.pl> Signed-off-by: Aubrey Li <aubrey.li@analog.com> Signed-off-by: Jie Zhang <jie.zhang@analog.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/blackfin/kernel/process.c')
-rw-r--r--arch/blackfin/kernel/process.c394
1 files changed, 394 insertions, 0 deletions
diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c
new file mode 100644
index 000000000000..3eff7439d8d3
--- /dev/null
+++ b/arch/blackfin/kernel/process.c
@@ -0,0 +1,394 @@
1/*
2 * File: arch/blackfin/kernel/process.c
3 * Based on:
4 * Author:
5 *
6 * Created:
7 * Description: Blackfin architecture-dependent process handling.
8 *
9 * Modified:
10 * Copyright 2004-2006 Analog Devices Inc.
11 *
12 * Bugs: Enter bugs at http://blackfin.uclinux.org/
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see the file COPYING, or write
26 * to the Free Software Foundation, Inc.,
27 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 */
29
30#include <linux/module.h>
31#include <linux/smp_lock.h>
32#include <linux/unistd.h>
33#include <linux/user.h>
34#include <linux/a.out.h>
35
36#include <asm/blackfin.h>
37#include <asm/uaccess.h>
38
39#define LED_ON 0
40#define LED_OFF 1
41
42asmlinkage void ret_from_fork(void);
43
44/* Points to the SDRAM backup memory for the stack that is currently in
45 * L1 scratchpad memory.
46 */
47void *current_l1_stack_save;
48
49/* The number of tasks currently using a L1 stack area. The SRAM is
50 * allocated/deallocated whenever this changes from/to zero.
51 */
52int nr_l1stack_tasks;
53
54/* Start and length of the area in L1 scratchpad memory which we've allocated
55 * for process stacks.
56 */
57void *l1_stack_base;
58unsigned long l1_stack_len;
59
60/*
61 * Powermanagement idle function, if any..
62 */
63void (*pm_idle)(void) = NULL;
64EXPORT_SYMBOL(pm_idle);
65
66void (*pm_power_off)(void) = NULL;
67EXPORT_SYMBOL(pm_power_off);
68
69/*
70 * We are using a different LED from the one used to indicate timer interrupt.
71 */
72#if defined(CONFIG_BFIN_IDLE_LED)
73static inline void leds_switch(int flag)
74{
75 unsigned short tmp = 0;
76
77 tmp = bfin_read_CONFIG_BFIN_IDLE_LED_PORT();
78 SSYNC();
79
80 if (flag == LED_ON)
81 tmp &= ~CONFIG_BFIN_IDLE_LED_PIN; /* light on */
82 else
83 tmp |= CONFIG_BFIN_IDLE_LED_PIN; /* light off */
84
85 bfin_write_CONFIG_BFIN_IDLE_LED_PORT(tmp);
86 SSYNC();
87
88}
89#else
90static inline void leds_switch(int flag)
91{
92}
93#endif
94
95/*
96 * The idle loop on BFIN
97 */
98#ifdef CONFIG_IDLE_L1
99void default_idle(void)__attribute__((l1_text));
100void cpu_idle(void)__attribute__((l1_text));
101#endif
102
103void default_idle(void)
104{
105 while (!need_resched()) {
106 leds_switch(LED_OFF);
107 local_irq_disable();
108 if (likely(!need_resched()))
109 idle_with_irq_disabled();
110 local_irq_enable();
111 leds_switch(LED_ON);
112 }
113}
114
115void (*idle)(void) = default_idle;
116
117/*
118 * The idle thread. There's no useful work to be
119 * done, so just try to conserve power and have a
120 * low exit latency (ie sit in a loop waiting for
121 * somebody to say that they'd like to reschedule)
122 */
123void cpu_idle(void)
124{
125 /* endless idle loop with no priority at all */
126 while (1) {
127 idle();
128 preempt_enable_no_resched();
129 schedule();
130 preempt_disable();
131 }
132}
133
134void machine_restart(char *__unused)
135{
136#if defined(CONFIG_BLKFIN_CACHE)
137 bfin_write_IMEM_CONTROL(0x01);
138 SSYNC();
139#endif
140 bfin_reset();
141 /* Dont do anything till the reset occurs */
142 while (1) {
143 SSYNC();
144 }
145}
146
147void machine_halt(void)
148{
149 for (;;)
150 asm volatile ("idle");
151}
152
153void machine_power_off(void)
154{
155 for (;;)
156 asm volatile ("idle");
157}
158
159void show_regs(struct pt_regs *regs)
160{
161 printk(KERN_NOTICE "\n");
162 printk(KERN_NOTICE
163 "PC: %08lu Status: %04lu SysStatus: %04lu RETS: %08lu\n",
164 regs->pc, regs->astat, regs->seqstat, regs->rets);
165 printk(KERN_NOTICE
166 "A0.x: %08lx A0.w: %08lx A1.x: %08lx A1.w: %08lx\n",
167 regs->a0x, regs->a0w, regs->a1x, regs->a1w);
168 printk(KERN_NOTICE "P0: %08lx P1: %08lx P2: %08lx P3: %08lx\n",
169 regs->p0, regs->p1, regs->p2, regs->p3);
170 printk(KERN_NOTICE "P4: %08lx P5: %08lx\n", regs->p4, regs->p5);
171 printk(KERN_NOTICE "R0: %08lx R1: %08lx R2: %08lx R3: %08lx\n",
172 regs->r0, regs->r1, regs->r2, regs->r3);
173 printk(KERN_NOTICE "R4: %08lx R5: %08lx R6: %08lx R7: %08lx\n",
174 regs->r4, regs->r5, regs->r6, regs->r7);
175
176 if (!(regs->ipend))
177 printk("USP: %08lx\n", rdusp());
178}
179
180/* Fill in the fpu structure for a core dump. */
181
182int dump_fpu(struct pt_regs *regs, elf_fpregset_t * fpregs)
183{
184 return 1;
185}
186
187/*
188 * This gets run with P1 containing the
189 * function to call, and R1 containing
190 * the "args". Note P0 is clobbered on the way here.
191 */
192void kernel_thread_helper(void);
193__asm__(".section .text\n"
194 ".align 4\n"
195 "_kernel_thread_helper:\n\t"
196 "\tsp += -12;\n\t"
197 "\tr0 = r1;\n\t" "\tcall (p1);\n\t" "\tcall _do_exit;\n" ".previous");
198
199/*
200 * Create a kernel thread.
201 */
202pid_t kernel_thread(int (*fn) (void *), void *arg, unsigned long flags)
203{
204 struct pt_regs regs;
205
206 memset(&regs, 0, sizeof(regs));
207
208 regs.r1 = (unsigned long)arg;
209 regs.p1 = (unsigned long)fn;
210 regs.pc = (unsigned long)kernel_thread_helper;
211 regs.orig_p0 = -1;
212 /* Set bit 2 to tell ret_from_fork we should be returning to kernel
213 mode. */
214 regs.ipend = 0x8002;
215 __asm__ __volatile__("%0 = syscfg;":"=da"(regs.syscfg):);
216 return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL,
217 NULL);
218}
219
220void flush_thread(void)
221{
222}
223
224asmlinkage int bfin_vfork(struct pt_regs *regs)
225{
226 return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL,
227 NULL);
228}
229
230asmlinkage int bfin_clone(struct pt_regs *regs)
231{
232 unsigned long clone_flags;
233 unsigned long newsp;
234
235 /* syscall2 puts clone_flags in r0 and usp in r1 */
236 clone_flags = regs->r0;
237 newsp = regs->r1;
238 if (!newsp)
239 newsp = rdusp();
240 else
241 newsp -= 12;
242 return do_fork(clone_flags, newsp, regs, 0, NULL, NULL);
243}
244
245int
246copy_thread(int nr, unsigned long clone_flags,
247 unsigned long usp, unsigned long topstk,
248 struct task_struct *p, struct pt_regs *regs)
249{
250 struct pt_regs *childregs;
251
252 childregs = (struct pt_regs *) (task_stack_page(p) + THREAD_SIZE) - 1;
253 *childregs = *regs;
254 childregs->r0 = 0;
255
256 p->thread.usp = usp;
257 p->thread.ksp = (unsigned long)childregs;
258 p->thread.pc = (unsigned long)ret_from_fork;
259
260 return 0;
261}
262
263/*
264 * fill in the user structure for a core dump..
265 */
266void dump_thread(struct pt_regs *regs, struct user *dump)
267{
268 dump->magic = CMAGIC;
269 dump->start_code = 0;
270 dump->start_stack = rdusp() & ~(PAGE_SIZE - 1);
271 dump->u_tsize = ((unsigned long)current->mm->end_code) >> PAGE_SHIFT;
272 dump->u_dsize = ((unsigned long)(current->mm->brk +
273 (PAGE_SIZE - 1))) >> PAGE_SHIFT;
274 dump->u_dsize -= dump->u_tsize;
275 dump->u_ssize = 0;
276
277 if (dump->start_stack < TASK_SIZE)
278 dump->u_ssize =
279 ((unsigned long)(TASK_SIZE -
280 dump->start_stack)) >> PAGE_SHIFT;
281
282 dump->u_ar0 = (struct user_regs_struct *)((int)&dump->regs - (int)dump);
283
284 dump->regs.r0 = regs->r0;
285 dump->regs.r1 = regs->r1;
286 dump->regs.r2 = regs->r2;
287 dump->regs.r3 = regs->r3;
288 dump->regs.r4 = regs->r4;
289 dump->regs.r5 = regs->r5;
290 dump->regs.r6 = regs->r6;
291 dump->regs.r7 = regs->r7;
292 dump->regs.p0 = regs->p0;
293 dump->regs.p1 = regs->p1;
294 dump->regs.p2 = regs->p2;
295 dump->regs.p3 = regs->p3;
296 dump->regs.p4 = regs->p4;
297 dump->regs.p5 = regs->p5;
298 dump->regs.orig_p0 = regs->orig_p0;
299 dump->regs.a0w = regs->a0w;
300 dump->regs.a1w = regs->a1w;
301 dump->regs.a0x = regs->a0x;
302 dump->regs.a1x = regs->a1x;
303 dump->regs.rets = regs->rets;
304 dump->regs.astat = regs->astat;
305 dump->regs.pc = regs->pc;
306}
307
308/*
309 * sys_execve() executes a new program.
310 */
311
312asmlinkage int sys_execve(char *name, char **argv, char **envp)
313{
314 int error;
315 char *filename;
316 struct pt_regs *regs = (struct pt_regs *)((&name) + 6);
317
318 lock_kernel();
319 filename = getname(name);
320 error = PTR_ERR(filename);
321 if (IS_ERR(filename))
322 goto out;
323 error = do_execve(filename, argv, envp, regs);
324 putname(filename);
325 out:
326 unlock_kernel();
327 return error;
328}
329
330unsigned long get_wchan(struct task_struct *p)
331{
332 unsigned long fp, pc;
333 unsigned long stack_page;
334 int count = 0;
335 if (!p || p == current || p->state == TASK_RUNNING)
336 return 0;
337
338 stack_page = (unsigned long)p;
339 fp = p->thread.usp;
340 do {
341 if (fp < stack_page + sizeof(struct thread_info) ||
342 fp >= 8184 + stack_page)
343 return 0;
344 pc = ((unsigned long *)fp)[1];
345 if (!in_sched_functions(pc))
346 return pc;
347 fp = *(unsigned long *)fp;
348 }
349 while (count++ < 16);
350 return 0;
351}
352
353#if defined(CONFIG_ACCESS_CHECK)
354int _access_ok(unsigned long addr, unsigned long size)
355{
356
357 if (addr > (addr + size))
358 return 0;
359 if (segment_eq(get_fs(),KERNEL_DS))
360 return 1;
361#ifdef CONFIG_MTD_UCLINUX
362 if (addr >= memory_start && (addr + size) <= memory_end)
363 return 1;
364 if (addr >= memory_mtd_end && (addr + size) <= physical_mem_end)
365 return 1;
366#else
367 if (addr >= memory_start && (addr + size) <= physical_mem_end)
368 return 1;
369#endif
370 if (addr >= (unsigned long)__init_begin &&
371 addr + size <= (unsigned long)__init_end)
372 return 1;
373 if (addr >= L1_SCRATCH_START
374 && addr + size <= L1_SCRATCH_START + L1_SCRATCH_LENGTH)
375 return 1;
376#if L1_CODE_LENGTH != 0
377 if (addr >= L1_CODE_START + (_etext_l1 - _stext_l1)
378 && addr + size <= L1_CODE_START + L1_CODE_LENGTH)
379 return 1;
380#endif
381#if L1_DATA_A_LENGTH != 0
382 if (addr >= L1_DATA_A_START + (_ebss_l1 - _sdata_l1)
383 && addr + size <= L1_DATA_A_START + L1_DATA_A_LENGTH)
384 return 1;
385#endif
386#if L1_DATA_B_LENGTH != 0
387 if (addr >= L1_DATA_B_START
388 && addr + size <= L1_DATA_B_START + L1_DATA_B_LENGTH)
389 return 1;
390#endif
391 return 0;
392}
393EXPORT_SYMBOL(_access_ok);
394#endif /* CONFIG_ACCESS_CHECK */