1/* 2 * linux/arch/arm26/kernel/fiq.c 3 * 4 * Copyright (C) 1998 Russell King 5 * Copyright (C) 1998, 1999 Phil Blundell 6 * Copyright (C) 2003 Ian Molton 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/mm.h> 41#include <linux/mman.h> 42#include <linux/init.h> 43#include <linux/seq_file.h> 44 45#include <asm/fiq.h> 46#include <asm/io.h> 47#include <asm/irq.h> 48#include <asm/pgalloc.h> 49#include <asm/system.h> 50#include <asm/uaccess.h> 51 52#define FIQ_VECTOR (vectors_base() + 0x1c) 53 54static unsigned long no_fiq_insn; 55 56#define unprotect_page_0() 57#define protect_page_0() 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 unprotect_page_0(); 67 *(unsigned long *)FIQ_VECTOR = no_fiq_insn; 68 protect_page_0(); 69 } 70 71 return 0; 72} 73 74static struct fiq_handler default_owner = { 75 .name = "default", 76 .fiq_op = fiq_def_op, 77}; 78 79static struct fiq_handler *current_fiq = &default_owner; 80 81int show_fiq_list(struct seq_file *p, void *v) 82{ 83 if (current_fiq != &default_owner) 84 seq_printf(p, "FIQ: %s\n", current_fiq->name); 85 86 return 0; 87} 88 89void set_fiq_handler(void *start, unsigned int length) 90{ 91 unprotect_page_0(); 92 93 memcpy((void *)FIQ_VECTOR, start, length); 94 95 protect_page_0(); 96} 97 98/* 99 * Taking an interrupt in FIQ mode is death, so both these functions 100 * disable irqs for the duration. 101 */ 102void set_fiq_regs(struct pt_regs *regs) 103{ 104 register unsigned long tmp, tmp2; 105 __asm__ volatile ( 106 "mov %0, pc \n" 107 "bic %1, %0, #0x3 \n" 108 "orr %1, %1, %3 \n" 109 "teqp %1, #0 @ select FIQ mode \n" 110 "mov r0, r0 \n" 111 "ldmia %2, {r8 - r14} \n" 112 "teqp %0, #0 @ return to SVC mode \n" 113 "mov r0, r0 " 114 : "=&r" (tmp), "=&r" (tmp2) 115 : "r" (®s->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | MODE_FIQ26) 116 /* These registers aren't modified by the above code in a way 117 visible to the compiler, but we mark them as clobbers anyway 118 so that GCC won't put any of the input or output operands in 119 them. */ 120 : "r8", "r9", "r10", "r11", "r12", "r13", "r14"); 121} 122 123void get_fiq_regs(struct pt_regs *regs) 124{ 125 register unsigned long tmp, tmp2; 126 __asm__ volatile ( 127 "mov %0, pc \n" 128 "bic %1, %0, #0x3 \n" 129 "orr %1, %1, %3 \n" 130 "teqp %1, #0 @ select FIQ mode \n" 131 "mov r0, r0 \n" 132 "stmia %2, {r8 - r14} \n" 133 "teqp %0, #0 @ return to SVC mode \n" 134 "mov r0, r0 " 135 : "=&r" (tmp), "=&r" (tmp2) 136 : "r" (®s->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | MODE_FIQ26) 137 /* These registers aren't modified by the above code in a way 138 visible to the compiler, but we mark them as clobbers anyway 139 so that GCC won't put any of the input or output operands in 140 them. */ 141 : "r8", "r9", "r10", "r11", "r12", "r13", "r14"); 142} 143 144int claim_fiq(struct fiq_handler *f) 145{ 146 int ret = 0; 147 148 if (current_fiq) { 149 ret = -EBUSY; 150 151 if (current_fiq->fiq_op != NULL) 152 ret = current_fiq->fiq_op(current_fiq->dev_id, 1); 153 } 154 155 if (!ret) { 156 f->next = current_fiq; 157 current_fiq = f; 158 } 159 160 return ret; 161} 162 163void release_fiq(struct fiq_handler *f) 164{ 165 if (current_fiq != f) { 166 printk(KERN_ERR "%s FIQ trying to release %s FIQ\n", 167 f->name, current_fiq->name); 168#ifdef CONFIG_DEBUG_ERRORS 169 __backtrace(); 170#endif 171 return; 172 } 173 174 do 175 current_fiq = current_fiq->next; 176 while (current_fiq->fiq_op(current_fiq->dev_id, 0)); 177} 178 179void enable_fiq(int fiq) 180{ 181 enable_irq(fiq + FIQ_START); 182} 183 184void disable_fiq(int fiq) 185{ 186 disable_irq(fiq + FIQ_START); 187} 188 189EXPORT_SYMBOL(set_fiq_handler); 190EXPORT_SYMBOL(set_fiq_regs); 191EXPORT_SYMBOL(get_fiq_regs); 192EXPORT_SYMBOL(claim_fiq); 193EXPORT_SYMBOL(release_fiq); 194EXPORT_SYMBOL(enable_fiq); 195EXPORT_SYMBOL(disable_fiq); 196 197void __init init_FIQ(void) 198{ 199 no_fiq_insn = *(unsigned long *)FIQ_VECTOR; 200 set_fs(get_fs()); 201} 202