linux/arch/arm/kernel/fiq.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *  linux/arch/arm/kernel/fiq.c
   4 *
   5 *  Copyright (C) 1998 Russell King
   6 *  Copyright (C) 1998, 1999 Phil Blundell
   7 *
   8 *  FIQ support written by Philip Blundell <philb@gnu.org>, 1998.
   9 *
  10 *  FIQ support re-written by Russell King to be more generic
  11 *
  12 * We now properly support a method by which the FIQ handlers can
  13 * be stacked onto the vector.  We still do not support sharing
  14 * the FIQ vector itself.
  15 *
  16 * Operation is as follows:
  17 *  1. Owner A claims FIQ:
  18 *     - default_fiq relinquishes control.
  19 *  2. Owner A:
  20 *     - inserts code.
  21 *     - sets any registers,
  22 *     - enables FIQ.
  23 *  3. Owner B claims FIQ:
  24 *     - if owner A has a relinquish function.
  25 *       - disable FIQs.
  26 *       - saves any registers.
  27 *       - returns zero.
  28 *  4. Owner B:
  29 *     - inserts code.
  30 *     - sets any registers,
  31 *     - enables FIQ.
  32 *  5. Owner B releases FIQ:
  33 *     - Owner A is asked to reacquire FIQ:
  34 *       - inserts code.
  35 *       - restores saved registers.
  36 *       - enables FIQ.
  37 *  6. Goto 3
  38 */
  39#include <linux/module.h>
  40#include <linux/kernel.h>
  41#include <linux/init.h>
  42#include <linux/interrupt.h>
  43#include <linux/seq_file.h>
  44
  45#include <asm/cacheflush.h>
  46#include <asm/cp15.h>
  47#include <asm/fiq.h>
  48#include <asm/irq.h>
  49#include <asm/traps.h>
  50
  51#define FIQ_OFFSET ({                                   \
  52                extern void *vector_fiq_offset;         \
  53                (unsigned)&vector_fiq_offset;           \
  54        })
  55
  56static unsigned long dfl_fiq_insn;
  57static struct pt_regs dfl_fiq_regs;
  58
  59/* Default reacquire function
  60 * - we always relinquish FIQ control
  61 * - we always reacquire FIQ control
  62 */
  63static int fiq_def_op(void *ref, int relinquish)
  64{
  65        if (!relinquish) {
  66                /* Restore default handler and registers */
  67                local_fiq_disable();
  68                set_fiq_regs(&dfl_fiq_regs);
  69                set_fiq_handler(&dfl_fiq_insn, sizeof(dfl_fiq_insn));
  70                local_fiq_enable();
  71
  72                /* FIXME: notify irq controller to standard enable FIQs */
  73        }
  74
  75        return 0;
  76}
  77
  78static struct fiq_handler default_owner = {
  79        .name   = "default",
  80        .fiq_op = fiq_def_op,
  81};
  82
  83static struct fiq_handler *current_fiq = &default_owner;
  84
  85int show_fiq_list(struct seq_file *p, int prec)
  86{
  87        if (current_fiq != &default_owner)
  88                seq_printf(p, "%*s:              %s\n", prec, "FIQ",
  89                        current_fiq->name);
  90
  91        return 0;
  92}
  93
  94void set_fiq_handler(void *start, unsigned int length)
  95{
  96        void *base = vectors_page;
  97        unsigned offset = FIQ_OFFSET;
  98
  99        memcpy(base + offset, start, length);
 100        if (!cache_is_vipt_nonaliasing())
 101                flush_icache_range((unsigned long)base + offset,
 102                                   (unsigned long)base + offset + length);
 103        flush_icache_range(0xffff0000 + offset, 0xffff0000 + offset + length);
 104}
 105
 106int claim_fiq(struct fiq_handler *f)
 107{
 108        int ret = 0;
 109
 110        if (current_fiq) {
 111                ret = -EBUSY;
 112
 113                if (current_fiq->fiq_op != NULL)
 114                        ret = current_fiq->fiq_op(current_fiq->dev_id, 1);
 115        }
 116
 117        if (!ret) {
 118                f->next = current_fiq;
 119                current_fiq = f;
 120        }
 121
 122        return ret;
 123}
 124
 125void release_fiq(struct fiq_handler *f)
 126{
 127        if (current_fiq != f) {
 128                pr_err("%s FIQ trying to release %s FIQ\n",
 129                       f->name, current_fiq->name);
 130                dump_stack();
 131                return;
 132        }
 133
 134        do
 135                current_fiq = current_fiq->next;
 136        while (current_fiq->fiq_op(current_fiq->dev_id, 0));
 137}
 138
 139static int fiq_start;
 140
 141void enable_fiq(int fiq)
 142{
 143        enable_irq(fiq + fiq_start);
 144}
 145
 146void disable_fiq(int fiq)
 147{
 148        disable_irq(fiq + fiq_start);
 149}
 150
 151EXPORT_SYMBOL(set_fiq_handler);
 152EXPORT_SYMBOL(__set_fiq_regs);  /* defined in fiqasm.S */
 153EXPORT_SYMBOL(__get_fiq_regs);  /* defined in fiqasm.S */
 154EXPORT_SYMBOL(claim_fiq);
 155EXPORT_SYMBOL(release_fiq);
 156EXPORT_SYMBOL(enable_fiq);
 157EXPORT_SYMBOL(disable_fiq);
 158
 159void __init init_FIQ(int start)
 160{
 161        unsigned offset = FIQ_OFFSET;
 162        dfl_fiq_insn = *(unsigned long *)(0xffff0000 + offset);
 163        get_fiq_regs(&dfl_fiq_regs);
 164        fiq_start = start;
 165}
 166