2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm26 / kernel / fiq . c
*
* Copyright ( C ) 1998 Russell King
* Copyright ( C ) 1998 , 1999 Phil Blundell
* Copyright ( C ) 2003 Ian Molton
*
* FIQ support written by Philip Blundell < philb @ gnu . org > , 1998.
*
* FIQ support re - written by Russell King to be more generic
*
* We now properly support a method by which the FIQ handlers can
* be stacked onto the vector . We still do not support sharing
* the FIQ vector itself .
*
* Operation is as follows :
* 1. Owner A claims FIQ :
* - default_fiq relinquishes control .
* 2. Owner A :
* - inserts code .
* - sets any registers ,
* - enables FIQ .
* 3. Owner B claims FIQ :
* - if owner A has a relinquish function .
* - disable FIQs .
* - saves any registers .
* - returns zero .
* 4. Owner B :
* - inserts code .
* - sets any registers ,
* - enables FIQ .
* 5. Owner B releases FIQ :
* - Owner A is asked to reacquire FIQ :
* - inserts code .
* - restores saved registers .
* - enables FIQ .
* 6. Goto 3
*/
# include <linux/module.h>
# include <linux/mm.h>
# include <linux/mman.h>
# include <linux/init.h>
# include <linux/seq_file.h>
# include <asm/fiq.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/pgalloc.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# define FIQ_VECTOR (vectors_base() + 0x1c)
static unsigned long no_fiq_insn ;
# define unprotect_page_0()
# define protect_page_0()
/* Default reacquire function
* - we always relinquish FIQ control
* - we always reacquire FIQ control
*/
static int fiq_def_op ( void * ref , int relinquish )
{
if ( ! relinquish ) {
unprotect_page_0 ( ) ;
* ( unsigned long * ) FIQ_VECTOR = no_fiq_insn ;
protect_page_0 ( ) ;
}
return 0 ;
}
static struct fiq_handler default_owner = {
. name = " default " ,
. fiq_op = fiq_def_op ,
} ;
static struct fiq_handler * current_fiq = & default_owner ;
int show_fiq_list ( struct seq_file * p , void * v )
{
if ( current_fiq ! = & default_owner )
seq_printf ( p , " FIQ: %s \n " , current_fiq - > name ) ;
return 0 ;
}
void set_fiq_handler ( void * start , unsigned int length )
{
unprotect_page_0 ( ) ;
memcpy ( ( void * ) FIQ_VECTOR , start , length ) ;
protect_page_0 ( ) ;
}
/*
* Taking an interrupt in FIQ mode is death , so both these functions
* disable irqs for the duration .
*/
void set_fiq_regs ( struct pt_regs * regs )
{
register unsigned long tmp , tmp2 ;
__asm__ volatile (
2006-02-01 14:06:19 +03:00
" mov %0, pc \n "
" bic %1, %0, #0x3 \n "
" orr %1, %1, %3 \n "
" teqp %1, #0 @ select FIQ mode \n "
" mov r0, r0 \n "
" ldmia %2, {r8 - r14} \n "
" teqp %0, #0 @ return to SVC mode \n "
" mov r0, r0 "
2005-04-17 02:20:36 +04:00
: " =&r " ( tmp ) , " =&r " ( tmp2 )
: " r " ( & regs - > ARM_r8 ) , " I " ( PSR_I_BIT | PSR_F_BIT | MODE_FIQ26 )
/* These registers aren't modified by the above code in a way
visible to the compiler , but we mark them as clobbers anyway
so that GCC won ' t put any of the input or output operands in
them . */
: " r8 " , " r9 " , " r10 " , " r11 " , " r12 " , " r13 " , " r14 " ) ;
}
void get_fiq_regs ( struct pt_regs * regs )
{
register unsigned long tmp , tmp2 ;
__asm__ volatile (
2006-02-01 14:06:19 +03:00
" mov %0, pc \n "
" bic %1, %0, #0x3 \n "
" orr %1, %1, %3 \n "
" teqp %1, #0 @ select FIQ mode \n "
" mov r0, r0 \n "
" stmia %2, {r8 - r14} \n "
" teqp %0, #0 @ return to SVC mode \n "
" mov r0, r0 "
2005-04-17 02:20:36 +04:00
: " =&r " ( tmp ) , " =&r " ( tmp2 )
: " r " ( & regs - > ARM_r8 ) , " I " ( PSR_I_BIT | PSR_F_BIT | MODE_FIQ26 )
/* These registers aren't modified by the above code in a way
visible to the compiler , but we mark them as clobbers anyway
so that GCC won ' t put any of the input or output operands in
them . */
: " r8 " , " r9 " , " r10 " , " r11 " , " r12 " , " r13 " , " r14 " ) ;
}
int claim_fiq ( struct fiq_handler * f )
{
int ret = 0 ;
if ( current_fiq ) {
ret = - EBUSY ;
if ( current_fiq - > fiq_op ! = NULL )
ret = current_fiq - > fiq_op ( current_fiq - > dev_id , 1 ) ;
}
if ( ! ret ) {
f - > next = current_fiq ;
current_fiq = f ;
}
return ret ;
}
void release_fiq ( struct fiq_handler * f )
{
if ( current_fiq ! = f ) {
printk ( KERN_ERR " %s FIQ trying to release %s FIQ \n " ,
f - > name , current_fiq - > name ) ;
# ifdef CONFIG_DEBUG_ERRORS
__backtrace ( ) ;
# endif
return ;
}
do
current_fiq = current_fiq - > next ;
while ( current_fiq - > fiq_op ( current_fiq - > dev_id , 0 ) ) ;
}
void enable_fiq ( int fiq )
{
enable_irq ( fiq + FIQ_START ) ;
}
void disable_fiq ( int fiq )
{
disable_irq ( fiq + FIQ_START ) ;
}
EXPORT_SYMBOL ( set_fiq_handler ) ;
EXPORT_SYMBOL ( set_fiq_regs ) ;
EXPORT_SYMBOL ( get_fiq_regs ) ;
EXPORT_SYMBOL ( claim_fiq ) ;
EXPORT_SYMBOL ( release_fiq ) ;
EXPORT_SYMBOL ( enable_fiq ) ;
EXPORT_SYMBOL ( disable_fiq ) ;
void __init init_FIQ ( void )
{
no_fiq_insn = * ( unsigned long * ) FIQ_VECTOR ;
set_fs ( get_fs ( ) ) ;
}