| 1 | /* |
| 2 | * from: vector.s, 386BSD 0.1 unknown origin |
| 3 | * $FreeBSD: src/sys/i386/isa/icu_vector.s,v 1.14.2.2 2000/07/18 21:12:42 dfr Exp $ |
| 4 | * $DragonFly: src/sys/platform/pc32/icu/icu_vector.s,v 1.33 2008/08/02 01:14:43 dillon Exp $ |
| 5 | */ |
| 6 | /* |
| 7 | * WARNING! SMP builds can use the ICU now so this code must be MP safe. |
| 8 | */ |
| 9 | |
| 10 | #include "use_npx.h" |
| 11 | #include "opt_auto_eoi.h" |
| 12 | |
| 13 | #include <machine/asmacros.h> |
| 14 | #include <machine/lock.h> |
| 15 | #include <machine/psl.h> |
| 16 | #include <machine/trap.h> |
| 17 | |
| 18 | #include <machine_base/icu/icu.h> |
| 19 | #include <bus/isa/isa.h> |
| 20 | |
| 21 | #include "assym.s" |
| 22 | #include "icu_ipl.h" |
| 23 | |
| 24 | #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ |
| 25 | |
| 26 | #define ICU_EOI 0x20 /* XXX - define elsewhere */ |
| 27 | |
| 28 | #define IRQ_LBIT(irq_num) (1 << (irq_num)) |
| 29 | #define IRQ_BIT(irq_num) (1 << ((irq_num) % 8)) |
| 30 | #define IRQ_BYTE(irq_num) ((irq_num) >> 3) |
| 31 | |
| 32 | #ifdef AUTO_EOI_1 |
| 33 | #define ENABLE_ICU1 /* use auto-EOI to reduce i/o */ |
| 34 | #define OUTB_ICU1 |
| 35 | #else |
| 36 | #define ENABLE_ICU1 \ |
| 37 | movb $ICU_EOI,%al ; /* as soon as possible send EOI ... */ \ |
| 38 | OUTB_ICU1 ; /* ... to clear in service bit */ \ |
| 39 | |
| 40 | #define OUTB_ICU1 \ |
| 41 | outb %al,$IO_ICU1 ; \ |
| 42 | |
| 43 | #endif |
| 44 | |
| 45 | #ifdef AUTO_EOI_2 |
| 46 | /* |
| 47 | * The data sheet says no auto-EOI on slave, but it sometimes works. |
| 48 | */ |
| 49 | #define ENABLE_ICU1_AND_2 ENABLE_ICU1 |
| 50 | #else |
| 51 | #define ENABLE_ICU1_AND_2 \ |
| 52 | movb $ICU_EOI,%al ; /* as above */ \ |
| 53 | outb %al,$IO_ICU2 ; /* but do second icu first ... */ \ |
| 54 | OUTB_ICU1 ; /* ... then first icu (if !AUTO_EOI_1) */ \ |
| 55 | |
| 56 | #endif |
| 57 | |
| 58 | /* |
| 59 | * Macro helpers |
| 60 | */ |
| 61 | #define PUSH_FRAME \ |
| 62 | pushl $0 ; /* dummy error code */ \ |
| 63 | pushl $0 ; /* dummy trap type */ \ |
| 64 | pushl $0 ; /* dummy xflags */ \ |
| 65 | pushal ; /* 8 registers */ \ |
| 66 | pushl %ds ; \ |
| 67 | pushl %es ; \ |
| 68 | pushl %fs ; \ |
| 69 | pushl %gs ; \ |
| 70 | cld ; \ |
| 71 | mov $KDSEL,%ax ; \ |
| 72 | mov %ax,%ds ; \ |
| 73 | mov %ax,%es ; \ |
| 74 | mov %ax,%gs ; \ |
| 75 | mov $KPSEL,%ax ; \ |
| 76 | mov %ax,%fs ; \ |
| 77 | |
| 78 | #define PUSH_DUMMY \ |
| 79 | pushfl ; /* phys int frame / flags */ \ |
| 80 | pushl %cs ; /* phys int frame / cs */ \ |
| 81 | pushl 12(%esp) ; /* original caller eip */ \ |
| 82 | pushl $0 ; /* dummy error code */ \ |
| 83 | pushl $0 ; /* dummy trap type */ \ |
| 84 | pushl $0 ; /* dummy xflags */ \ |
| 85 | subl $13*4,%esp ; /* pushal + 4 seg regs (dummy) + CPL */ \ |
| 86 | |
| 87 | /* |
| 88 | * Warning: POP_FRAME can only be used if there is no chance of a |
| 89 | * segment register being changed (e.g. by procfs), which is why syscalls |
| 90 | * have to use doreti. |
| 91 | */ |
| 92 | #define POP_FRAME \ |
| 93 | popl %gs ; \ |
| 94 | popl %fs ; \ |
| 95 | popl %es ; \ |
| 96 | popl %ds ; \ |
| 97 | popal ; \ |
| 98 | addl $2*4,%esp ; /* dummy trap & error codes */ \ |
| 99 | |
| 100 | #define POP_DUMMY \ |
| 101 | addl $19*4,%esp ; \ |
| 102 | |
| 103 | #define MASK_IRQ(icu, irq_num) \ |
| 104 | ICU_IMASK_LOCK ; \ |
| 105 | movb icu_imen + IRQ_BYTE(irq_num),%al ; \ |
| 106 | orb $IRQ_BIT(irq_num),%al ; \ |
| 107 | movb %al,icu_imen + IRQ_BYTE(irq_num) ; \ |
| 108 | outb %al,$icu+ICU_IMR_OFFSET ; \ |
| 109 | ICU_IMASK_UNLOCK ; \ |
| 110 | |
| 111 | #define UNMASK_IRQ(icu, irq_num) \ |
| 112 | cmpl $0,%eax ; \ |
| 113 | jnz 8f ; \ |
| 114 | ICU_IMASK_LOCK ; \ |
| 115 | movb icu_imen + IRQ_BYTE(irq_num),%al ; \ |
| 116 | andb $~IRQ_BIT(irq_num),%al ; \ |
| 117 | movb %al,icu_imen + IRQ_BYTE(irq_num) ; \ |
| 118 | outb %al,$icu+ICU_IMR_OFFSET ; \ |
| 119 | ICU_IMASK_UNLOCK ; \ |
| 120 | 8: ; \ |
| 121 | |
| 122 | /* |
| 123 | * Fast interrupt call handlers run in the following sequence: |
| 124 | * |
| 125 | * - Push the trap frame required by doreti. |
| 126 | * - Mask the interrupt and reenable its source. |
| 127 | * - If we cannot take the interrupt set its fpending bit and |
| 128 | * doreti. |
| 129 | * - If we can take the interrupt clear its fpending bit, |
| 130 | * call the handler, then unmask the interrupt and doreti. |
| 131 | * |
| 132 | * YYY can cache gd base pointer instead of using hidden %fs |
| 133 | * prefixes. |
| 134 | */ |
| 135 | |
| 136 | #define FAST_INTR(irq_num, vec_name, icu, enable_icus) \ |
| 137 | .text ; \ |
| 138 | SUPERALIGN_TEXT ; \ |
| 139 | IDTVEC(vec_name) ; \ |
| 140 | PUSH_FRAME ; \ |
| 141 | FAKE_MCOUNT(15*4(%esp)) ; \ |
| 142 | MASK_IRQ(icu, irq_num) ; \ |
| 143 | enable_icus ; \ |
| 144 | movl PCPU(curthread),%ebx ; \ |
| 145 | pushl $0 ; /* DUMMY CPL FOR DORETI */ \ |
| 146 | testl $-1,TD_NEST_COUNT(%ebx) ; \ |
| 147 | jne 1f ; \ |
| 148 | testl $-1,TD_CRITCOUNT(%ebx) ; \ |
| 149 | je 2f ; \ |
| 150 | 1: ; \ |
| 151 | /* set pending bit and return, leave interrupt masked */ \ |
| 152 | orl $IRQ_LBIT(irq_num),PCPU(fpending) ; \ |
| 153 | orl $RQF_INTPEND, PCPU(reqflags) ; \ |
| 154 | jmp 5f ; \ |
| 155 | 2: ; \ |
| 156 | /* clear pending bit, run handler */ \ |
| 157 | andl $~IRQ_LBIT(irq_num),PCPU(fpending) ; \ |
| 158 | pushl $irq_num ; \ |
| 159 | pushl %esp ; /* pass frame by reference */ \ |
| 160 | incl TD_CRITCOUNT(%ebx) ; \ |
| 161 | call ithread_fast_handler ; /* returns 0 to unmask int */ \ |
| 162 | decl TD_CRITCOUNT(%ebx) ; \ |
| 163 | addl $8,%esp ; \ |
| 164 | UNMASK_IRQ(icu, irq_num) ; \ |
| 165 | 5: ; \ |
| 166 | MEXITCOUNT ; \ |
| 167 | jmp doreti ; \ |
| 168 | |
| 169 | /* |
| 170 | * Unmask a slow interrupt. This function is used by interrupt threads |
| 171 | * after they have descheduled themselves to reenable interrupts and |
| 172 | * possibly cause a reschedule to occur. |
| 173 | */ |
| 174 | |
| 175 | #define INTR_UNMASK(irq_num, vec_name, icu) \ |
| 176 | .text ; \ |
| 177 | SUPERALIGN_TEXT ; \ |
| 178 | IDTVEC(vec_name) ; \ |
| 179 | pushl %ebp ; /* frame for ddb backtrace */ \ |
| 180 | movl %esp, %ebp ; \ |
| 181 | subl %eax, %eax ; \ |
| 182 | UNMASK_IRQ(icu, irq_num) ; \ |
| 183 | popl %ebp ; \ |
| 184 | ret ; \ |
| 185 | |
| 186 | MCOUNT_LABEL(bintr) |
| 187 | FAST_INTR(0,icu_fastintr0, IO_ICU1, ENABLE_ICU1) |
| 188 | FAST_INTR(1,icu_fastintr1, IO_ICU1, ENABLE_ICU1) |
| 189 | FAST_INTR(2,icu_fastintr2, IO_ICU1, ENABLE_ICU1) |
| 190 | FAST_INTR(3,icu_fastintr3, IO_ICU1, ENABLE_ICU1) |
| 191 | FAST_INTR(4,icu_fastintr4, IO_ICU1, ENABLE_ICU1) |
| 192 | FAST_INTR(5,icu_fastintr5, IO_ICU1, ENABLE_ICU1) |
| 193 | FAST_INTR(6,icu_fastintr6, IO_ICU1, ENABLE_ICU1) |
| 194 | FAST_INTR(7,icu_fastintr7, IO_ICU1, ENABLE_ICU1) |
| 195 | FAST_INTR(8,icu_fastintr8, IO_ICU2, ENABLE_ICU1_AND_2) |
| 196 | FAST_INTR(9,icu_fastintr9, IO_ICU2, ENABLE_ICU1_AND_2) |
| 197 | FAST_INTR(10,icu_fastintr10, IO_ICU2, ENABLE_ICU1_AND_2) |
| 198 | FAST_INTR(11,icu_fastintr11, IO_ICU2, ENABLE_ICU1_AND_2) |
| 199 | FAST_INTR(12,icu_fastintr12, IO_ICU2, ENABLE_ICU1_AND_2) |
| 200 | FAST_INTR(13,icu_fastintr13, IO_ICU2, ENABLE_ICU1_AND_2) |
| 201 | FAST_INTR(14,icu_fastintr14, IO_ICU2, ENABLE_ICU1_AND_2) |
| 202 | FAST_INTR(15,icu_fastintr15, IO_ICU2, ENABLE_ICU1_AND_2) |
| 203 | MCOUNT_LABEL(eintr) |
| 204 | |
| 205 | .data |
| 206 | |
| 207 | .text |