2012-03-05 11:49:32 +00:00
/*
* FP / SIMD context switching and fault handling
*
* Copyright ( C ) 2012 ARM Ltd .
* Author : Catalin Marinas < catalin . marinas @ arm . 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2013-07-19 17:48:08 +01:00
# include <linux/cpu_pm.h>
2012-03-05 11:49:32 +00:00
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/signal.h>
2013-07-09 14:18:12 +01:00
# include <linux/hardirq.h>
2012-03-05 11:49:32 +00:00
# include <asm/fpsimd.h>
# include <asm/cputype.h>
# define FPEXC_IOF (1 << 0)
# define FPEXC_DZF (1 << 1)
# define FPEXC_OFF (1 << 2)
# define FPEXC_UFF (1 << 3)
# define FPEXC_IXF (1 << 4)
# define FPEXC_IDF (1 << 7)
/*
* Trapped FP / ASIMD access .
*/
void do_fpsimd_acc ( unsigned int esr , struct pt_regs * regs )
{
/* TODO: implement lazy context saving/restoring */
WARN_ON ( 1 ) ;
}
/*
* Raise a SIGFPE for the current process .
*/
void do_fpsimd_exc ( unsigned int esr , struct pt_regs * regs )
{
siginfo_t info ;
unsigned int si_code = 0 ;
if ( esr & FPEXC_IOF )
si_code = FPE_FLTINV ;
else if ( esr & FPEXC_DZF )
si_code = FPE_FLTDIV ;
else if ( esr & FPEXC_OFF )
si_code = FPE_FLTOVF ;
else if ( esr & FPEXC_UFF )
si_code = FPE_FLTUND ;
else if ( esr & FPEXC_IXF )
si_code = FPE_FLTRES ;
memset ( & info , 0 , sizeof ( info ) ) ;
info . si_signo = SIGFPE ;
info . si_code = si_code ;
info . si_addr = ( void __user * ) instruction_pointer ( regs ) ;
send_sig_info ( SIGFPE , & info , current ) ;
}
void fpsimd_thread_switch ( struct task_struct * next )
{
/* check if not kernel threads */
if ( current - > mm )
fpsimd_save_state ( & current - > thread . fpsimd_state ) ;
if ( next - > mm )
fpsimd_load_state ( & next - > thread . fpsimd_state ) ;
}
void fpsimd_flush_thread ( void )
{
2013-09-27 09:04:41 +01:00
preempt_disable ( ) ;
2012-03-05 11:49:32 +00:00
memset ( & current - > thread . fpsimd_state , 0 , sizeof ( struct fpsimd_state ) ) ;
fpsimd_load_state ( & current - > thread . fpsimd_state ) ;
2013-09-27 09:04:41 +01:00
preempt_enable ( ) ;
2012-03-05 11:49:32 +00:00
}
2013-07-09 14:18:12 +01:00
# ifdef CONFIG_KERNEL_MODE_NEON
/*
* Kernel - side NEON support functions
*/
void kernel_neon_begin ( void )
{
/* Avoid using the NEON in interrupt context */
BUG_ON ( in_interrupt ( ) ) ;
preempt_disable ( ) ;
if ( current - > mm )
fpsimd_save_state ( & current - > thread . fpsimd_state ) ;
}
EXPORT_SYMBOL ( kernel_neon_begin ) ;
void kernel_neon_end ( void )
{
if ( current - > mm )
fpsimd_load_state ( & current - > thread . fpsimd_state ) ;
preempt_enable ( ) ;
}
EXPORT_SYMBOL ( kernel_neon_end ) ;
# endif /* CONFIG_KERNEL_MODE_NEON */
2013-07-19 17:48:08 +01:00
# ifdef CONFIG_CPU_PM
static int fpsimd_cpu_pm_notifier ( struct notifier_block * self ,
unsigned long cmd , void * v )
{
switch ( cmd ) {
case CPU_PM_ENTER :
if ( current - > mm )
fpsimd_save_state ( & current - > thread . fpsimd_state ) ;
break ;
case CPU_PM_EXIT :
if ( current - > mm )
fpsimd_load_state ( & current - > thread . fpsimd_state ) ;
break ;
case CPU_PM_ENTER_FAILED :
default :
return NOTIFY_DONE ;
}
return NOTIFY_OK ;
}
static struct notifier_block fpsimd_cpu_pm_notifier_block = {
. notifier_call = fpsimd_cpu_pm_notifier ,
} ;
static void fpsimd_pm_init ( void )
{
cpu_pm_register_notifier ( & fpsimd_cpu_pm_notifier_block ) ;
}
# else
static inline void fpsimd_pm_init ( void ) { }
# endif /* CONFIG_CPU_PM */
2012-03-05 11:49:32 +00:00
/*
* FP / SIMD support code initialisation .
*/
static int __init fpsimd_init ( void )
{
u64 pfr = read_cpuid ( ID_AA64PFR0_EL1 ) ;
if ( pfr & ( 0xf < < 16 ) ) {
pr_notice ( " Floating-point is not implemented \n " ) ;
return 0 ;
}
elf_hwcap | = HWCAP_FP ;
if ( pfr & ( 0xf < < 20 ) )
pr_notice ( " Advanced SIMD is not implemented \n " ) ;
else
elf_hwcap | = HWCAP_ASIMD ;
2013-07-19 17:48:08 +01:00
fpsimd_pm_init ( ) ;
2012-03-05 11:49:32 +00:00
return 0 ;
}
late_initcall ( fpsimd_init ) ;