linux/arch/arm/kernel/fiq.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/kernel/fiq.c
   3 *
   4 *  Copyright (C) 1998 Russell King
   5 *  Copyright (C) 1998, 1999 Phil Blundell
   6 *
   7 *  FIQ support written by Philip Blundell <philb@gnu.org>, 1998.
   8 *
   9 *  FIQ support re-written by Russell King to be more generic
  10 *
  11 * We now properly support a method by which the FIQ handlers can
  12 * be stacked onto the vector.  We still do not support sharing
  13 * the FIQ vector itself.
  14 *
  15 * Operation is as follows:
  16 *  1. Owner A claims FIQ:
  17 *     - default_fiq relinquishes control.
  18 *  2. Owner A:
  19 *     - inserts code.
  20 *     - sets any registers,
  21 *     - enables FIQ.
  22 *  3. Owner B claims FIQ:
  23 *     - if owner A has a relinquish function.
  24 *       - disable FIQs.
  25 *       - saves any registers.
  26 *       - returns zero.
  27 *  4. Owner B:
  28 *     - inserts code.
  29 *     - sets any registers,
  30 *     - enables FIQ.
  31 *  5. Owner B releases FIQ:
  32 *     - Owner A is asked to reacquire FIQ:
  33 *       - inserts code.
  34 *       - restores saved registers.
  35 *       - enables FIQ.
  36 *  6. Goto 3
  37 */
  38#include <linux/module.h>
  39#include <linux/kernel.h>
  40#include <linux/init.h>
  41#include <linux/interrupt.h>
  42#include <linux/seq_file.h>
  43
  44#include <asm/cacheflush.h>
  45#include <asm/fiq.h>
  46#include <asm/irq.h>
  47#include <asm/system.h>
  48
  49static unsigned long no_fiq_insn;
  50
  51/* Default reacquire function
  52 * - we always relinquish FIQ control
  53 * - we always reacquire FIQ control
  54 */
  55static int fiq_def_op(void *ref, int relinquish)
  56{
  57        if (!relinquish)
  58                set_fiq_handler(&no_fiq_insn, sizeof(no_fiq_insn));
  59
  60        return 0;
  61}
  62
  63static struct fiq_handler default_owner = {
  64        .name   = "default",
  65        .fiq_op = fiq_def_op,
  66};
  67
  68static struct fiq_handler *current_fiq = &default_owner;
  69
  70int show_fiq_list(struct seq_file *p, void *v)
  71{
  72        if (current_fiq != &default_owner)
  73                seq_printf(p, "FIQ:              %s\n", current_fiq->name);
  74
  75        return 0;
  76}
  77
  78void set_fiq_handler(void *start, unsigned int length)
  79{
  80        memcpy((void *)0xffff001c, start, length);
  81        flush_icache_range(0xffff001c, 0xffff001c + length);
  82        if (!vectors_high())
  83                flush_icache_range(0x1c, 0x1c + length);
  84}
  85
  86/*
  87 * Taking an interrupt in FIQ mode is death, so both these functions
  88 * disable irqs for the duration.  Note - these functions are almost
  89 * entirely coded in assembly.
  90 */
  91void __naked set_fiq_regs(struct pt_regs *regs)
  92{
  93        register unsigned long tmp;
  94        asm volatile (
  95        "mov    ip, sp\n\
  96        stmfd   sp!, {fp, ip, lr, pc}\n\
  97        sub     fp, ip, #4\n\
  98        mrs     %0, cpsr\n\
  99        msr     cpsr_c, %2      @ select FIQ mode\n\
 100        mov     r0, r0\n\
 101        ldmia   %1, {r8 - r14}\n\
 102        msr     cpsr_c, %0      @ return to SVC mode\n\
 103        mov     r0, r0\n\
 104        ldmfd   sp, {fp, sp, pc}"
 105        : "=&r" (tmp)
 106        : "r" (&regs->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE));
 107}
 108
 109void __naked get_fiq_regs(struct pt_regs *regs)
 110{
 111        register unsigned long tmp;
 112        asm volatile (
 113        "mov    ip, sp\n\
 114        stmfd   sp!, {fp, ip, lr, pc}\n\
 115        sub     fp, ip, #4\n\
 116        mrs     %0, cpsr\n\
 117        msr     cpsr_c, %2      @ select FIQ mode\n\
 118        mov     r0, r0\n\
 119        stmia   %1, {r8 - r14}\n\
 120        msr     cpsr_c, %0      @ return to SVC mode\n\
 121        mov     r0, r0\n\
 122        ldmfd   sp, {fp, sp, pc}"
 123        : "=&r" (tmp)
 124        : "r" (&regs->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE));
 125}
 126
 127int claim_fiq(struct fiq_handler *f)
 128{
 129        int ret = 0;
 130
 131        if (current_fiq) {
 132                ret = -EBUSY;
 133
 134                if (current_fiq->fiq_op != NULL)
 135                        ret = current_fiq->fiq_op(current_fiq->dev_id, 1);
 136        }
 137
 138        if (!ret) {
 139                f->next = current_fiq;
 140                current_fiq = f;
 141        }
 142
 143        return ret;
 144}
 145
 146void release_fiq(struct fiq_handler *f)
 147{
 148        if (current_fiq != f) {
 149                printk(KERN_ERR "%s FIQ trying to release %s FIQ\n",
 150                       f->name, current_fiq->name);
 151                dump_stack();
 152                return;
 153        }
 154
 155        do
 156                current_fiq = current_fiq->next;
 157        while (current_fiq->fiq_op(current_fiq->dev_id, 0));
 158}
 159
 160void enable_fiq(int fiq)
 161{
 162        enable_irq(fiq + FIQ_START);
 163}
 164
 165void disable_fiq(int fiq)
 166{
 167        disable_irq(fiq + FIQ_START);
 168}
 169
 170EXPORT_SYMBOL(set_fiq_handler);
 171EXPORT_SYMBOL(set_fiq_regs);
 172EXPORT_SYMBOL(get_fiq_regs);
 173EXPORT_SYMBOL(claim_fiq);
 174EXPORT_SYMBOL(release_fiq);
 175EXPORT_SYMBOL(enable_fiq);
 176EXPORT_SYMBOL(disable_fiq);
 177
 178void __init init_FIQ(void)
 179{
 180        no_fiq_insn = *(unsigned long *)0xffff001c;
 181}
 182
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.