Branch data Line data Source code
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 : : 56 : : static unsigned long dfl_fiq_insn; 57 : : static struct pt_regs dfl_fiq_regs; 58 : : 59 : : extern int irq_activate(struct irq_desc *desc); 60 : : 61 : : /* Default reacquire function 62 : : * - we always relinquish FIQ control 63 : : * - we always reacquire FIQ control 64 : : */ 65 : 207 : static int fiq_def_op(void *ref, int relinquish) 66 : : { 67 [ - + ]: 207 : if (!relinquish) { 68 : : /* Restore default handler and registers */ 69 : 0 : local_fiq_disable(); 70 : : set_fiq_regs(&dfl_fiq_regs); 71 : 0 : set_fiq_handler(&dfl_fiq_insn, sizeof(dfl_fiq_insn)); 72 : 0 : local_fiq_enable(); 73 : : 74 : : /* FIXME: notify irq controller to standard enable FIQs */ 75 : : } 76 : : 77 : 207 : return 0; 78 : : } 79 : : 80 : : static struct fiq_handler default_owner = { 81 : : .name = "default", 82 : : .fiq_op = fiq_def_op, 83 : : }; 84 : : 85 : : static struct fiq_handler *current_fiq = &default_owner; 86 : : 87 : 0 : int show_fiq_list(struct seq_file *p, int prec) 88 : : { 89 [ # # ]: 0 : if (current_fiq != &default_owner) 90 : 0 : seq_printf(p, "%*s: %s\n", prec, "FIQ", 91 : : current_fiq->name); 92 : : 93 : 0 : return 0; 94 : : } 95 : : 96 : 207 : void set_fiq_handler(void *start, unsigned int length) 97 : : { 98 : 207 : void *base = vectors_page; 99 : 207 : unsigned offset = FIQ_OFFSET; 100 : : 101 : 207 : memcpy(base + offset, start, length); 102 [ - + ]: 207 : if (!cache_is_vipt_nonaliasing()) 103 : 0 : flush_icache_range((unsigned long)base + offset, offset + 104 : : length); 105 : 207 : flush_icache_range(0xffff0000 + offset, 0xffff0000 + offset + length); 106 : 207 : } 107 : : 108 : 207 : int claim_fiq(struct fiq_handler *f) 109 : : { 110 : : int ret = 0; 111 : : 112 [ + - ]: 207 : if (current_fiq) { 113 : : ret = -EBUSY; 114 : : 115 [ + - ]: 207 : if (current_fiq->fiq_op != NULL) 116 : 207 : ret = current_fiq->fiq_op(current_fiq->dev_id, 1); 117 : : } 118 : : 119 [ + - ]: 207 : if (!ret) { 120 : 207 : f->next = current_fiq; 121 : 207 : current_fiq = f; 122 : : } 123 : : 124 : 207 : return ret; 125 : : } 126 : : 127 : 0 : void release_fiq(struct fiq_handler *f) 128 : : { 129 [ # # ]: 0 : if (current_fiq != f) { 130 : 0 : pr_err("%s FIQ trying to release %s FIQ\n", 131 : : f->name, current_fiq->name); 132 : 0 : dump_stack(); 133 : 0 : return; 134 : : } 135 : : 136 : : do 137 : 0 : current_fiq = current_fiq->next; 138 [ # # ]: 0 : while (current_fiq->fiq_op(current_fiq->dev_id, 0)); 139 : : } 140 : : 141 : : static int fiq_start; 142 : : 143 : 207 : void enable_fiq(int fiq) 144 : : { 145 : 207 : struct irq_desc *desc = irq_to_desc(fiq + fiq_start); 146 : 207 : irq_activate(desc); 147 : 207 : enable_irq(fiq + fiq_start); 148 : 207 : } 149 : : 150 : 0 : void disable_fiq(int fiq) 151 : : { 152 : 0 : disable_irq(fiq + fiq_start); 153 : 0 : } 154 : : 155 : : EXPORT_SYMBOL(set_fiq_handler); 156 : : EXPORT_SYMBOL(__set_fiq_regs); /* defined in fiqasm.S */ 157 : : EXPORT_SYMBOL(__get_fiq_regs); /* defined in fiqasm.S */ 158 : : EXPORT_SYMBOL(claim_fiq); 159 : : EXPORT_SYMBOL(release_fiq); 160 : : EXPORT_SYMBOL(enable_fiq); 161 : : EXPORT_SYMBOL(disable_fiq); 162 : : 163 : 207 : void __init init_FIQ(int start) 164 : : { 165 : 207 : unsigned offset = FIQ_OFFSET; 166 : 207 : dfl_fiq_insn = *(unsigned long *)(0xffff0000 + offset); 167 : : get_fiq_regs(&dfl_fiq_regs); 168 : 207 : fiq_start = start; 169 : 207 : }