1123992Ssobomax// SPDX-License-Identifier: GPL-2.0-only 2103026Ssobomax/* 3139823Simp * linux/arch/arm/kernel/xscale-cp0.c 4103026Ssobomax * 5103026Ssobomax * XScale DSP and iWMMXt coprocessor context switching and handling 6103026Ssobomax */ 7103026Ssobomax 8103026Ssobomax#include <linux/types.h> 9103026Ssobomax#include <linux/kernel.h> 10148613Sbz#include <linux/signal.h> 11148613Sbz#include <linux/sched.h> 12103026Ssobomax#include <linux/init.h> 13103026Ssobomax#include <linux/io.h> 14103026Ssobomax#include <asm/thread_notify.h> 15103026Ssobomax#include <asm/cputype.h> 16103026Ssobomax 17103026Ssobomaxasm(" .arch armv5te\n"); 18103026Ssobomax 19103026Ssobomaxstatic inline void dsp_save_state(u32 *state) 20103026Ssobomax{ 21103026Ssobomax __asm__ __volatile__ ( 22103026Ssobomax "mrrc p0, 0, %0, %1, c0\n" 23103026Ssobomax : "=r" (state[0]), "=r" (state[1])); 24103026Ssobomax} 25103026Ssobomax 26103026Ssobomaxstatic inline void dsp_load_state(u32 *state) 27103026Ssobomax{ 28103026Ssobomax __asm__ __volatile__ ( 29103026Ssobomax "mcrr p0, 0, %0, %1, c0\n" 30103026Ssobomax : : "r" (state[0]), "r" (state[1])); 31103026Ssobomax} 32103026Ssobomax 33103026Ssobomaxstatic int dsp_do(struct notifier_block *self, unsigned long cmd, void *t) 34103026Ssobomax{ 35103026Ssobomax struct thread_info *thread = t; 36103026Ssobomax 37103026Ssobomax switch (cmd) { 38103026Ssobomax case THREAD_NOTIFY_FLUSH: 39103026Ssobomax thread->cpu_context.extra[0] = 0; 40172467Ssilby thread->cpu_context.extra[1] = 0; 41172467Ssilby break; 42172467Ssilby 43103026Ssobomax case THREAD_NOTIFY_SWITCH: 44103026Ssobomax dsp_save_state(current_thread_info()->cpu_context.extra); 45148613Sbz dsp_load_state(thread->cpu_context.extra); 46103026Ssobomax break; 47103026Ssobomax } 48103026Ssobomax 49103026Ssobomax return NOTIFY_DONE; 50103026Ssobomax} 51103026Ssobomax 52103026Ssobomaxstatic struct notifier_block dsp_notifier_block = { 53103026Ssobomax .notifier_call = dsp_do, 54103026Ssobomax}; 55103026Ssobomax 56103026Ssobomax 57103026Ssobomax#ifdef CONFIG_IWMMXT 58103026Ssobomaxstatic int iwmmxt_do(struct notifier_block *self, unsigned long cmd, void *t) 59103026Ssobomax{ 60103026Ssobomax struct thread_info *thread = t; 61103026Ssobomax 62103026Ssobomax switch (cmd) { 63103026Ssobomax case THREAD_NOTIFY_FLUSH: 64103026Ssobomax /* 65103026Ssobomax * flush_thread() zeroes thread->fpstate, so no need 66103026Ssobomax * to do anything here. 67103026Ssobomax * 68103026Ssobomax * FALLTHROUGH: Ensure we don't try to overwrite our newly 69103026Ssobomax * initialised state information on the first fault. 70103026Ssobomax */ 71103026Ssobomax 72103026Ssobomax case THREAD_NOTIFY_EXIT: 73230452Sbz iwmmxt_task_release(thread); 74103026Ssobomax break; 75103026Ssobomax 76103026Ssobomax case THREAD_NOTIFY_SWITCH: 77103026Ssobomax iwmmxt_task_switch(thread); 78103026Ssobomax break; 79103026Ssobomax } 80103026Ssobomax 81103026Ssobomax return NOTIFY_DONE; 82103026Ssobomax} 83103026Ssobomax 84103026Ssobomaxstatic struct notifier_block iwmmxt_notifier_block = { 85103026Ssobomax .notifier_call = iwmmxt_do, 86103026Ssobomax}; 87103026Ssobomax#endif 88133874Srwatson 89103026Ssobomax 90103026Ssobomaxstatic u32 __init xscale_cp_access_read(void) 91105301Salfred{ 92103026Ssobomax u32 value; 93158645Sglebius 94103026Ssobomax __asm__ __volatile__ ( 95103026Ssobomax "mrc p15, 0, %0, c15, c1, 0\n\t" 96103026Ssobomax : "=r" (value)); 97103026Ssobomax 98103026Ssobomax return value; 99103026Ssobomax} 100103026Ssobomax 101103026Ssobomaxstatic void __init xscale_cp_access_write(u32 value) 102154625Sbz{ 103103026Ssobomax u32 temp; 104158645Sglebius 105103026Ssobomax __asm__ __volatile__ ( 106103026Ssobomax "mcr p15, 0, %1, c15, c1, 0\n\t" 107103026Ssobomax "mrc p15, 0, %0, c15, c1, 0\n\t" 108158645Sglebius "mov %0, %0\n\t" 109158645Sglebius "sub pc, pc, #4\n\t" 110103026Ssobomax : "=r" (temp) : "r" (value)); 111158645Sglebius} 112158645Sglebius 113103026Ssobomax/* 114158645Sglebius * Detect whether we have a MAC coprocessor (40 bit register) or an 115103026Ssobomax * iWMMXt coprocessor (64 bit registers) by loading 00000100:00000000 116103026Ssobomax * into a coprocessor register and reading it back, and checking 117103026Ssobomax * whether the upper word survived intact. 118103026Ssobomax */ 119158645Sglebiusstatic int __init cpu_has_iwmmxt(void) 120158645Sglebius{ 121158645Sglebius u32 lo; 122158645Sglebius u32 hi; 123103026Ssobomax 124158645Sglebius /* 125103026Ssobomax * This sequence is interpreted by the DSP coprocessor as: 126103026Ssobomax * mar acc0, %2, %3 127123992Ssobomax * mra %0, %1, acc0 128111888Sjlemon * 129103026Ssobomax * And by the iWMMXt coprocessor as: 130123992Ssobomax * tmcrr wR0, %2, %3 131148613Sbz * tmrrc %0, %1, wR0 132103026Ssobomax */ 133103026Ssobomax __asm__ __volatile__ ( 134103026Ssobomax "mcrr p0, 0, %2, %3, c0\n" 135158645Sglebius "mrrc p0, 0, %0, %1, c0\n" 136103026Ssobomax : "=r" (lo), "=r" (hi) 137103026Ssobomax : "r" (0), "r" (0x100)); 138123992Ssobomax 139123992Ssobomax return !!hi; 140123992Ssobomax} 141158645Sglebius 142123992Ssobomax 143123992Ssobomax/* 144123992Ssobomax * If we detect that the CPU has iWMMXt (and CONFIG_IWMMXT=y), we 145147256Sbrooks * disable CP0/CP1 on boot, and let call_fpe() and the iWMMXt lazy 146147256Sbrooks * switch code handle iWMMXt context switching. If on the other 147103026Ssobomax * hand the CPU has a DSP coprocessor, we keep access to CP0 enabled 148103026Ssobomax * all the time, and save/restore acc0 on context switch in non-lazy 149103026Ssobomax * fashion. 150123992Ssobomax */ 151103026Ssobomaxstatic int __init xscale_cp0_init(void) 152103026Ssobomax{ 153103026Ssobomax u32 cp_access; 154103026Ssobomax 155103026Ssobomax /* do not attempt to probe iwmmxt on non-xscale family CPUs */ 156103026Ssobomax if (!cpu_is_xscale_family()) 157103026Ssobomax return 0; 158103026Ssobomax 159103026Ssobomax cp_access = xscale_cp_access_read() & ~3; 160158645Sglebius xscale_cp_access_write(cp_access | 1); 161103026Ssobomax 162103026Ssobomax if (cpu_has_iwmmxt()) { 163103026Ssobomax#ifndef CONFIG_IWMMXT 164123992Ssobomax pr_warn("CAUTION: XScale iWMMXt coprocessor detected, but kernel support is missing.\n"); 165103026Ssobomax#else 166103026Ssobomax pr_info("XScale iWMMXt coprocessor detected.\n"); 167125024Ssobomax elf_hwcap |= HWCAP_IWMMXT; 168125024Ssobomax thread_register_notifier(&iwmmxt_notifier_block); 169125024Ssobomax register_iwmmxt_undef_handler(); 170125024Ssobomax#endif 171125024Ssobomax } else { 172125024Ssobomax pr_info("XScale DSP coprocessor detected.\n"); 173148613Sbz thread_register_notifier(&dsp_notifier_block); 174103026Ssobomax cp_access |= 1; 175148613Sbz } 176148613Sbz 177148613Sbz xscale_cp_access_write(cp_access); 178148613Sbz 179148613Sbz return 0; 180148613Sbz} 181103026Ssobomax 182103026Ssobomaxlate_initcall(xscale_cp0_init); 183111888Sjlemon