Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 687b12ba authored by Aurelien Jacquiot's avatar Aurelien Jacquiot Committed by Mark Salter
Browse files

C6X: process management



Original port to early 2.6 kernel using TI COFF toolchain.
Brought up to date by Mark Salter <msalter@redhat.com>

Signed-off-by: default avatarAurelien Jacquiot <a-jacquiot@ti.com>
Signed-off-by: default avatarMark Salter <msalter@redhat.com>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
parent 14aa7e8b
Loading
Loading
Loading
Loading
+132 −0
Original line number Diff line number Diff line
/*
 *  Port on Texas Instruments TMS320C6x architecture
 *
 *  Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated
 *  Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com)
 *
 *  Updated for 2.6.34: Mark Salter <msalter@redhat.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.
 */
#ifndef _ASM_C6X_PROCESSOR_H
#define _ASM_C6X_PROCESSOR_H

#include <asm/ptrace.h>
#include <asm/page.h>
#include <asm/current.h>

/*
 * Default implementation of macro that returns current
 * instruction pointer ("program counter").
 */
#define current_text_addr()			\
({						\
	void *__pc;				\
	asm("mvc .S2 pce1,%0\n" : "=b"(__pc));	\
	__pc;					\
})

/*
 * User space process size. This is mostly meaningless for NOMMU
 * but some C6X processors may have RAM addresses up to 0xFFFFFFFF.
 * Since calls like mmap() can return an address or an error, we
 * have to allow room for error returns when code does something
 * like:
 *
 *       addr = do_mmap(...)
 *       if ((unsigned long)addr >= TASK_SIZE)
 *            ... its an error code, not an address ...
 *
 * Here, we allow for 4096 error codes which means we really can't
 * use the last 4K page on systems with RAM extending all the way
 * to the end of the 32-bit address space.
 */
#define TASK_SIZE	0xFFFFF000

/*
 * This decides where the kernel will search for a free chunk of vm
 * space during mmap's. We won't be using it
 */
#define TASK_UNMAPPED_BASE	0

struct thread_struct {
	unsigned long long b15_14;
	unsigned long long a15_14;
	unsigned long long b13_12;
	unsigned long long a13_12;
	unsigned long long b11_10;
	unsigned long long a11_10;
	unsigned long long ricl_icl;
	unsigned long  usp;		/* user stack pointer */
	unsigned long  pc;		/* kernel pc */
	unsigned long  wchan;
};

#define INIT_THREAD					\
{							\
	.usp = 0,					\
	.wchan = 0,					\
}

#define INIT_MMAP { \
	&init_mm, 0, 0, NULL, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, 1, \
	NULL, NULL }

#define task_pt_regs(task) \
	((struct pt_regs *)(THREAD_START_SP + task_stack_page(task)) - 1)

#define alloc_kernel_stack()	__get_free_page(GFP_KERNEL)
#define free_kernel_stack(page) free_page((page))


/* Forward declaration, a strange C thing */
struct task_struct;

extern void start_thread(struct pt_regs *regs, unsigned int pc,
			 unsigned long usp);

/* Free all resources held by a thread. */
static inline void release_thread(struct task_struct *dead_task)
{
}

/* Prepare to copy thread state - unlazy all lazy status */
#define prepare_to_copy(tsk)	do { } while (0)

extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);

#define copy_segments(tsk, mm)		do { } while (0)
#define release_segments(mm)		do { } while (0)

/*
 * saved PC of a blocked thread.
 */
#define thread_saved_pc(tsk) (task_pt_regs(tsk)->pc)

/*
 * saved kernel SP and DP of a blocked thread.
 */
#ifdef _BIG_ENDIAN
#define thread_saved_ksp(tsk) \
	(*(unsigned long *)&(tsk)->thread.b15_14)
#define thread_saved_dp(tsk) \
	(*(((unsigned long *)&(tsk)->thread.b15_14) + 1))
#else
#define thread_saved_ksp(tsk) \
	(*(((unsigned long *)&(tsk)->thread.b15_14) + 1))
#define thread_saved_dp(tsk) \
	(*(unsigned long *)&(tsk)->thread.b15_14)
#endif

extern unsigned long get_wchan(struct task_struct *p);

#define KSTK_EIP(tsk)	(task_pt_regs(task)->pc)
#define	KSTK_ESP(tsk)	(task_pt_regs(task)->sp)

#define cpu_relax()		do { } while (0)

extern const struct seq_operations cpuinfo_op;

#endif /* ASM_C6X_PROCESSOR_H */
+121 −0
Original line number Diff line number Diff line
/*
 *  Port on Texas Instruments TMS320C6x architecture
 *
 *  Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated
 *  Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com)
 *
 *  Updated for 2.6.3x: Mark Salter <msalter@redhat.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.
 */
#ifndef _ASM_C6X_THREAD_INFO_H
#define _ASM_C6X_THREAD_INFO_H

#ifdef __KERNEL__

#include <asm/page.h>

#ifdef CONFIG_4KSTACKS
#define THREAD_SIZE		4096
#define THREAD_SHIFT		12
#define THREAD_ORDER		0
#else
#define THREAD_SIZE		8192
#define THREAD_SHIFT		13
#define THREAD_ORDER		1
#endif

#define THREAD_START_SP		(THREAD_SIZE - 8)

#ifndef __ASSEMBLY__

typedef struct {
	unsigned long seg;
} mm_segment_t;

/*
 * low level task data.
 */
struct thread_info {
	struct task_struct	*task;		/* main task structure */
	struct exec_domain	*exec_domain;	/* execution domain */
	unsigned long		flags;		/* low level flags */
	int			cpu;		/* cpu we're on */
	int			preempt_count;	/* 0 = preemptable, <0 = BUG */
	mm_segment_t		addr_limit;	/* thread address space */
	struct restart_block	restart_block;
};

/*
 * macros/functions for gaining access to the thread information structure
 *
 * preempt_count needs to be 1 initially, until the scheduler is functional.
 */
#define INIT_THREAD_INFO(tsk)			\
{						\
	.task		= &tsk,			\
	.exec_domain	= &default_exec_domain,	\
	.flags		= 0,			\
	.cpu		= 0,			\
	.preempt_count	= INIT_PREEMPT_COUNT,	\
	.addr_limit	= KERNEL_DS,		\
	.restart_block	= {			\
		.fn = do_no_restart_syscall,	\
	},					\
}

#define init_thread_info	(init_thread_union.thread_info)
#define init_stack		(init_thread_union.stack)

/* get the thread information struct of current task */
static inline __attribute__((const))
struct thread_info *current_thread_info(void)
{
	struct thread_info *ti;
	asm volatile (" clr   .s2 B15,0,%1,%0\n"
		      : "=b" (ti)
		      : "Iu5" (THREAD_SHIFT - 1));
	return ti;
}

#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR

/* thread information allocation */
#ifdef CONFIG_DEBUG_STACK_USAGE
#define THREAD_FLAGS (GFP_KERNEL | __GFP_NOTRACK | __GFP_ZERO)
#else
#define THREAD_FLAGS (GFP_KERNEL | __GFP_NOTRACK)
#endif

#define alloc_thread_info_node(tsk, node)	\
	((struct thread_info *)__get_free_pages(THREAD_FLAGS, THREAD_ORDER))

#define free_thread_info(ti)	free_pages((unsigned long) (ti), THREAD_ORDER)
#define get_thread_info(ti)	get_task_struct((ti)->task)
#define put_thread_info(ti)	put_task_struct((ti)->task)
#endif /* __ASSEMBLY__ */

#define	PREEMPT_ACTIVE	0x10000000

/*
 * thread information flag bit numbers
 * - pending work-to-be-done flags are in LSW
 * - other flags in MSW
 */
#define TIF_SYSCALL_TRACE	0	/* syscall trace active */
#define TIF_NOTIFY_RESUME	1	/* resumption notification requested */
#define TIF_SIGPENDING		2	/* signal pending */
#define TIF_NEED_RESCHED	3	/* rescheduling necessary */
#define TIF_RESTORE_SIGMASK	4	/* restore signal mask in do_signal() */

#define TIF_POLLING_NRFLAG	16	/* true if polling TIF_NEED_RESCHED */
#define TIF_MEMDIE		17	/* OOM killer killed process */

#define TIF_WORK_MASK		0x00007FFE /* work on irq/exception return */
#define TIF_ALLWORK_MASK	0x00007FFF /* work on any return to u-space */

#endif /* __KERNEL__ */

#endif /* _ASM_C6X_THREAD_INFO_H */
+263 −0
Original line number Diff line number Diff line
/*
 *  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);

static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);

/*
 * Initial thread structure.
 */
union thread_union init_thread_union __init_task_data =	{
	INIT_THREAD_INFO(init_task)
};

/*
 * Initial task structure.
 */
struct task_struct init_task = INIT_TASK(init_task);
EXPORT_SYMBOL(init_task);

/*
 * 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_stop_sched_tick(1);
		while (1) {
			local_irq_disable();
			if (need_resched()) {
				local_irq_enable();
				break;
			}
			c6x_idle(); /* enables local irqs */
		}
		tick_nohz_restart_sched_tick();

		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(&regs, 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, &regs,
		       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;
}
+74 −0
Original line number Diff line number Diff line
/*
 *  Copyright (C) 2011 Texas Instruments Incorporated
 *  Author: Mark Salter (msalter@redhat.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/linkage.h>
#include <asm/asm-offsets.h>

#define SP	B15

	/*
	 * void __switch_to(struct thread_info *prev,
	 *      	    struct thread_info *next,
	 *		    struct task_struct *tsk) ;
	 */
ENTRY(__switch_to)
	LDDW	.D2T2	*+B4(THREAD_B15_14),B7:B6
 ||	MV	.L2X	A4,B5	; prev
 ||	MV	.L1X	B4,A5	; next
 ||	MVC	.S2	RILC,B1

	STW	.D2T2	B3,*+B5(THREAD_PC)
 ||	STDW	.D1T1	A13:A12,*+A4(THREAD_A13_12)
 ||	MVC	.S2	ILC,B0

	LDW	.D2T2	*+B4(THREAD_PC),B3
 ||	LDDW	.D1T1	*+A5(THREAD_A13_12),A13:A12

	STDW	.D1T1	A11:A10,*+A4(THREAD_A11_10)
 ||	STDW	.D2T2	B1:B0,*+B5(THREAD_RICL_ICL)
#ifndef __DSBT__
 ||	MVKL	.S2	current_ksp,B1
#endif

	STDW	.D2T2	B15:B14,*+B5(THREAD_B15_14)
 ||	STDW	.D1T1	A15:A14,*+A4(THREAD_A15_14)
#ifndef __DSBT__
 ||	MVKH	.S2	current_ksp,B1
#endif

	;; Switch to next SP
	MV	.S2	B7,SP
#ifdef __DSBT__
 ||	STW	.D2T2	B7,*+B14(current_ksp)
#else
 ||	STW	.D2T2	B7,*B1
 ||	MV	.L2	B6,B14
#endif
 ||	LDDW	.D1T1	*+A5(THREAD_RICL_ICL),A1:A0

	STDW	.D2T2	B11:B10,*+B5(THREAD_B11_10)
 ||	LDDW	.D1T1	*+A5(THREAD_A15_14),A15:A14

	STDW	.D2T2	B13:B12,*+B5(THREAD_B13_12)
 ||	LDDW	.D1T1	*+A5(THREAD_A11_10),A11:A10

	B	.S2	B3		; return in next E1
 ||	LDDW	.D2T2	*+B4(THREAD_B13_12),B13:B12

	LDDW	.D2T2	*+B4(THREAD_B11_10),B11:B10
	NOP

	MV	.L2X	A0,B0
 ||	MV	.S1	A6,A4

	MVC	.S2	B0,ILC
 ||	MV	.L2X	A1,B1

	MVC	.S2	B1,RILC
ENDPROC(__switch_to)