| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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> | 
| Thomas Gleixner | 4a2581a | 2006-07-01 22:30:09 +0100 | [diff] [blame] | 41 | #include <linux/interrupt.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 48 |  | 
|  | 49 | static unsigned long no_fiq_insn; | 
|  | 50 |  | 
|  | 51 | /* Default reacquire function | 
|  | 52 | * - we always relinquish FIQ control | 
|  | 53 | * - we always reacquire FIQ control | 
|  | 54 | */ | 
|  | 55 | static 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 |  | 
|  | 63 | static struct fiq_handler default_owner = { | 
|  | 64 | .name	= "default", | 
|  | 65 | .fiq_op = fiq_def_op, | 
|  | 66 | }; | 
|  | 67 |  | 
|  | 68 | static struct fiq_handler *current_fiq = &default_owner; | 
|  | 69 |  | 
|  | 70 | int 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 |  | 
|  | 78 | void 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 | */ | 
| Uwe Kleine-König | 446c92b | 2009-03-12 18:03:16 +0100 | [diff] [blame] | 91 | void __naked set_fiq_regs(struct pt_regs *regs) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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\ | 
| Catalin Marinas | 90303b1 | 2006-01-12 16:53:51 +0000 | [diff] [blame] | 104 | ldmfd	sp, {fp, sp, pc}" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 105 | : "=&r" (tmp) | 
|  | 106 | : "r" (®s->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE)); | 
|  | 107 | } | 
|  | 108 |  | 
| Uwe Kleine-König | 446c92b | 2009-03-12 18:03:16 +0100 | [diff] [blame] | 109 | void __naked get_fiq_regs(struct pt_regs *regs) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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\ | 
| Catalin Marinas | 90303b1 | 2006-01-12 16:53:51 +0000 | [diff] [blame] | 122 | ldmfd	sp, {fp, sp, pc}" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 123 | : "=&r" (tmp) | 
|  | 124 | : "r" (®s->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE)); | 
|  | 125 | } | 
|  | 126 |  | 
|  | 127 | int 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 |  | 
|  | 146 | void 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 |  | 
|  | 160 | void enable_fiq(int fiq) | 
|  | 161 | { | 
|  | 162 | enable_irq(fiq + FIQ_START); | 
|  | 163 | } | 
|  | 164 |  | 
|  | 165 | void disable_fiq(int fiq) | 
|  | 166 | { | 
|  | 167 | disable_irq(fiq + FIQ_START); | 
|  | 168 | } | 
|  | 169 |  | 
|  | 170 | EXPORT_SYMBOL(set_fiq_handler); | 
|  | 171 | EXPORT_SYMBOL(set_fiq_regs); | 
|  | 172 | EXPORT_SYMBOL(get_fiq_regs); | 
|  | 173 | EXPORT_SYMBOL(claim_fiq); | 
|  | 174 | EXPORT_SYMBOL(release_fiq); | 
|  | 175 | EXPORT_SYMBOL(enable_fiq); | 
|  | 176 | EXPORT_SYMBOL(disable_fiq); | 
|  | 177 |  | 
|  | 178 | void __init init_FIQ(void) | 
|  | 179 | { | 
|  | 180 | no_fiq_insn = *(unsigned long *)0xffff001c; | 
|  | 181 | } |