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 ) ;
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 ) {
2012-01-08 13:25:56 -05:00
tick_nohz_idle_enter ( ) ;
rcu_idle_enter ( ) ;
2011-10-04 11:03:44 -04:00
while ( 1 ) {
local_irq_disable ( ) ;
if ( need_resched ( ) ) {
local_irq_enable ( ) ;
break ;
}
c6x_idle ( ) ; /* enables local irqs */
}
2012-01-08 13:25:56 -05:00
rcu_idle_exit ( ) ;
tick_nohz_idle_exit ( ) ;
2011-10-04 11:03:44 -04:00
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 ( ) ;
}
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 ;
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 ,
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 ;
}