linux/arch/arm/kernel/xscale-cp0.c
<<
>>
Prefs
   1/*
   2 * linux/arch/arm/kernel/xscale-cp0.c
   3 *
   4 * XScale DSP and iWMMXt coprocessor context switching and handling
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10
  11#include <linux/types.h>
  12#include <linux/kernel.h>
  13#include <linux/signal.h>
  14#include <linux/sched.h>
  15#include <linux/init.h>
  16#include <linux/io.h>
  17#include <asm/thread_notify.h>
  18#include <asm/cputype.h>
  19
  20asm("   .arch armv5te\n");
  21
  22static inline void dsp_save_state(u32 *state)
  23{
  24        __asm__ __volatile__ (
  25                "mrrc   p0, 0, %0, %1, c0\n"
  26                : "=r" (state[0]), "=r" (state[1]));
  27}
  28
  29static inline void dsp_load_state(u32 *state)
  30{
  31        __asm__ __volatile__ (
  32                "mcrr   p0, 0, %0, %1, c0\n"
  33                : : "r" (state[0]), "r" (state[1]));
  34}
  35
  36static int dsp_do(struct notifier_block *self, unsigned long cmd, void *t)
  37{
  38        struct thread_info *thread = t;
  39
  40        switch (cmd) {
  41        case THREAD_NOTIFY_FLUSH:
  42                thread->cpu_context.extra[0] = 0;
  43                thread->cpu_context.extra[1] = 0;
  44                break;
  45
  46        case THREAD_NOTIFY_SWITCH:
  47                dsp_save_state(current_thread_info()->cpu_context.extra);
  48                dsp_load_state(thread->cpu_context.extra);
  49                break;
  50        }
  51
  52        return NOTIFY_DONE;
  53}
  54
  55static struct notifier_block dsp_notifier_block = {
  56        .notifier_call  = dsp_do,
  57};
  58
  59
  60#ifdef CONFIG_IWMMXT
  61static int iwmmxt_do(struct notifier_block *self, unsigned long cmd, void *t)
  62{
  63        struct thread_info *thread = t;
  64
  65        switch (cmd) {
  66        case THREAD_NOTIFY_FLUSH:
  67                /*
  68                 * flush_thread() zeroes thread->fpstate, so no need
  69                 * to do anything here.
  70                 *
  71                 * FALLTHROUGH: Ensure we don't try to overwrite our newly
  72                 * initialised state information on the first fault.
  73                 */
  74
  75        case THREAD_NOTIFY_EXIT:
  76                iwmmxt_task_release(thread);
  77                break;
  78
  79        case THREAD_NOTIFY_SWITCH:
  80                iwmmxt_task_switch(thread);
  81                break;
  82        }
  83
  84        return NOTIFY_DONE;
  85}
  86
  87static struct notifier_block iwmmxt_notifier_block = {
  88        .notifier_call  = iwmmxt_do,
  89};
  90#endif
  91
  92
  93static u32 __init xscale_cp_access_read(void)
  94{
  95        u32 value;
  96
  97        __asm__ __volatile__ (
  98                "mrc    p15, 0, %0, c15, c1, 0\n\t"
  99                : "=r" (value));
 100
 101        return value;
 102}
 103
 104static void __init xscale_cp_access_write(u32 value)
 105{
 106        u32 temp;
 107
 108        __asm__ __volatile__ (
 109                "mcr    p15, 0, %1, c15, c1, 0\n\t"
 110                "mrc    p15, 0, %0, c15, c1, 0\n\t"
 111                "mov    %0, %0\n\t"
 112                "sub    pc, pc, #4\n\t"
 113                : "=r" (temp) : "r" (value));
 114}
 115
 116/*
 117 * Detect whether we have a MAC coprocessor (40 bit register) or an
 118 * iWMMXt coprocessor (64 bit registers) by loading 00000100:00000000
 119 * into a coprocessor register and reading it back, and checking
 120 * whether the upper word survived intact.
 121 */
 122static int __init cpu_has_iwmmxt(void)
 123{
 124        u32 lo;
 125        u32 hi;
 126
 127        /*
 128         * This sequence is interpreted by the DSP coprocessor as:
 129         *      mar     acc0, %2, %3
 130         *      mra     %0, %1, acc0
 131         *
 132         * And by the iWMMXt coprocessor as:
 133         *      tmcrr   wR0, %2, %3
 134         *      tmrrc   %0, %1, wR0
 135         */
 136        __asm__ __volatile__ (
 137                "mcrr   p0, 0, %2, %3, c0\n"
 138                "mrrc   p0, 0, %0, %1, c0\n"
 139                : "=r" (lo), "=r" (hi)
 140                : "r" (0), "r" (0x100));
 141
 142        return !!hi;
 143}
 144
 145
 146/*
 147 * If we detect that the CPU has iWMMXt (and CONFIG_IWMMXT=y), we
 148 * disable CP0/CP1 on boot, and let call_fpe() and the iWMMXt lazy
 149 * switch code handle iWMMXt context switching.  If on the other
 150 * hand the CPU has a DSP coprocessor, we keep access to CP0 enabled
 151 * all the time, and save/restore acc0 on context switch in non-lazy
 152 * fashion.
 153 */
 154static int __init xscale_cp0_init(void)
 155{
 156        u32 cp_access;
 157
 158        /* do not attempt to probe iwmmxt on non-xscale family CPUs */
 159        if (!cpu_is_xscale_family())
 160                return 0;
 161
 162        cp_access = xscale_cp_access_read() & ~3;
 163        xscale_cp_access_write(cp_access | 1);
 164
 165        if (cpu_has_iwmmxt()) {
 166#ifndef CONFIG_IWMMXT
 167                pr_warn("CAUTION: XScale iWMMXt coprocessor detected, but kernel support is missing.\n");
 168#else
 169                pr_info("XScale iWMMXt coprocessor detected.\n");
 170                elf_hwcap |= HWCAP_IWMMXT;
 171                thread_register_notifier(&iwmmxt_notifier_block);
 172#endif
 173        } else {
 174                pr_info("XScale DSP coprocessor detected.\n");
 175                thread_register_notifier(&dsp_notifier_block);
 176                cp_access |= 1;
 177        }
 178
 179        xscale_cp_access_write(cp_access);
 180
 181        return 0;
 182}
 183
 184late_initcall(xscale_cp0_init);
 185