Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / sys / platform / pc32 / isa / apic_ipl.s
1 /*-
2  * Copyright (c) 1997, by Steve Passe
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. The name of the developer may NOT be used to endorse or promote products
11  *    derived from this software without specific prior written permission.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD: src/sys/i386/isa/apic_ipl.s,v 1.27.2.2 2000/09/30 02:49:35 ps Exp $
26  * $DragonFly: src/sys/platform/pc32/isa/Attic/apic_ipl.s,v 1.2 2003/06/17 04:28:36 dillon Exp $
27  */
28
29
30         .data
31         ALIGN_DATA
32
33 /*
34  * Routines used by splz_unpend to build an interrupt frame from a
35  * trap frame.  The _vec[] routines build the proper frame on the stack,
36  * then call one of _Xintr0 thru _XintrNN.
37  *
38  * used by:
39  *   i386/isa/apic_ipl.s (this file):   splz_unpend JUMPs to HWIs.
40  *   i386/isa/clock.c:                  setup _vec[clock] to point at _vec8254.
41  */
42         .globl _vec
43 _vec:
44         .long    vec0,  vec1,  vec2,  vec3,  vec4,  vec5,  vec6,  vec7
45         .long    vec8,  vec9, vec10, vec11, vec12, vec13, vec14, vec15
46         .long   vec16, vec17, vec18, vec19, vec20, vec21, vec22, vec23
47
48 /*
49  * Note:
50  *      This is the UP equivilant of _imen.
51  *      It is OPAQUE, and must NOT be accessed directly.
52  *      It MUST be accessed along with the IO APIC as a 'critical region'.
53  *      Accessed by:
54  *              INTREN()
55  *              INTRDIS()
56  *              MAYBE_MASK_IRQ
57  *              MAYBE_UNMASK_IRQ
58  *              imen_dump()
59  */
60         .p2align 2                              /* MUST be 32bit aligned */
61         .globl _apic_imen
62 _apic_imen:
63         .long   HWI_MASK
64
65
66 /*
67  * 
68  */
69         .text
70         SUPERALIGN_TEXT
71
72 /*
73  * splz() -     dispatch pending interrupts after cpl reduced
74  *
75  * Interrupt priority mechanism
76  *      -- soft splXX masks with group mechanism (cpl)
77  *      -- h/w masks for currently active or unused interrupts (imen)
78  *      -- ipending = active interrupts currently masked by cpl
79  */
80
81 ENTRY(splz)
82         /*
83          * The caller has restored cpl and checked that (ipending & ~cpl)
84          * is nonzero.  However, since ipending can change at any time
85          * (by an interrupt or, with SMP, by another cpu), we have to
86          * repeat the check.  At the moment we must own the MP lock in
87          * the SMP case because the interruput handlers require it.  We
88          * loop until no unmasked pending interrupts remain.  
89          *
90          * No new unmaksed pending interrupts will be added during the
91          * loop because, being unmasked, the interrupt code will be able
92          * to execute the interrupts.
93          *
94          * Interrupts come in two flavors:  Hardware interrupts and software
95          * interrupts.  We have to detect the type of interrupt (based on the
96          * position of the interrupt bit) and call the appropriate dispatch
97          * routine.
98          * 
99          * NOTE: "bsfl %ecx,%ecx" is undefined when %ecx is 0 so we can't
100          * rely on the secondary btrl tests.
101          */
102         movl    _cpl,%eax
103 splz_next:
104         /*
105          * We don't need any locking here.  (ipending & ~cpl) cannot grow 
106          * while we're looking at it - any interrupt will shrink it to 0.
107          */
108         movl    %eax,%ecx
109         notl    %ecx                    /* set bit = unmasked level */
110         andl    _ipending,%ecx          /* set bit = unmasked pending INT */
111         jne     splz_unpend
112         ret
113
114         ALIGN_TEXT
115 splz_unpend:
116         bsfl    %ecx,%ecx
117         lock
118         btrl    %ecx,_ipending
119         jnc     splz_next
120         cmpl    $NHWI,%ecx
121         jae     splz_swi
122         /*
123          * We would prefer to call the intr handler directly here but that
124          * doesn't work for badly behaved handlers that want the interrupt
125          * frame.  Also, there's a problem determining the unit number.
126          * We should change the interface so that the unit number is not
127          * determined at config time.
128          *
129          * The vec[] routines build the proper frame on the stack,
130          * then call one of _Xintr0 thru _XintrNN.
131          */
132         jmp     *_vec(,%ecx,4)
133
134         ALIGN_TEXT
135 splz_swi:
136         pushl   %eax
137         orl     imasks(,%ecx,4),%eax
138         movl    %eax,_cpl
139         call    *_ihandlers(,%ecx,4)
140         popl    %eax
141         movl    %eax,_cpl
142         jmp     splz_next
143
144 /*
145  * Fake clock interrupt(s) so that they appear to come from our caller instead
146  * of from here, so that system profiling works.
147  * XXX do this more generally (for all vectors; look up the C entry point).
148  * XXX frame bogusness stops us from just jumping to the C entry point.
149  * We have to clear iactive since this is an unpend call, and it will be
150  * set from the time of the original INT.
151  */
152
153 /*
154  * The 'generic' vector stubs.
155  */
156
157 #define BUILD_VEC(irq_num)                                              \
158         ALIGN_TEXT ;                                                    \
159 __CONCAT(vec,irq_num): ;                                                \
160         popl    %eax ;                                                  \
161         pushfl ;                                                        \
162         pushl   $KCSEL ;                                                \
163         pushl   %eax ;                                                  \
164         cli ;                                                           \
165         lock ;                                  /* MP-safe */           \
166         andl    $~IRQ_BIT(irq_num), iactive ;   /* lazy masking */      \
167         MEXITCOUNT ;                                                    \
168         APIC_ITRACE(apic_itrace_splz, irq_num, APIC_ITRACE_SPLZ) ;      \
169         jmp     __CONCAT(_Xintr,irq_num)
170
171
172         BUILD_VEC(0)
173         BUILD_VEC(1)
174         BUILD_VEC(2)
175         BUILD_VEC(3)
176         BUILD_VEC(4)
177         BUILD_VEC(5)
178         BUILD_VEC(6)
179         BUILD_VEC(7)
180         BUILD_VEC(8)
181         BUILD_VEC(9)
182         BUILD_VEC(10)
183         BUILD_VEC(11)
184         BUILD_VEC(12)
185         BUILD_VEC(13)
186         BUILD_VEC(14)
187         BUILD_VEC(15)
188         BUILD_VEC(16)                   /* 8 additional INTs in IO APIC */
189         BUILD_VEC(17)
190         BUILD_VEC(18)
191         BUILD_VEC(19)
192         BUILD_VEC(20)
193         BUILD_VEC(21)
194         BUILD_VEC(22)
195         BUILD_VEC(23)
196
197
198 /******************************************************************************
199  * XXX FIXME: figure out where these belong.
200  */
201
202 /* this nonsense is to verify that masks ALWAYS have 1 and only 1 bit set */
203 #define QUALIFY_MASKS_NOT
204
205 #ifdef QUALIFY_MASKS
206 #define QUALIFY_MASK            \
207         btrl    %ecx, %eax ;    \
208         andl    %eax, %eax ;    \
209         jz      1f ;            \
210         pushl   $bad_mask ;     \
211         call    _panic ;        \
212 1:
213
214 bad_mask:       .asciz  "bad mask"
215 #else
216 #define QUALIFY_MASK
217 #endif
218
219 /*
220  * (soon to be) MP-safe function to clear ONE INT mask bit.
221  * The passed arg is a 32bit u_int MASK.
222  * It sets the associated bit in _apic_imen.
223  * It sets the mask bit of the associated IO APIC register.
224  */
225 ENTRY(INTREN)
226         pushfl                          /* save state of EI flag */
227         cli                             /* prevent recursion */
228         IMASK_LOCK                      /* enter critical reg */
229
230         movl    8(%esp), %eax           /* mask into %eax */
231         bsfl    %eax, %ecx              /* get pin index */
232         btrl    %ecx, _apic_imen        /* update _apic_imen */
233
234         QUALIFY_MASK
235
236         shll    $4, %ecx
237         movl    CNAME(int_to_apicintpin) + 8(%ecx), %edx
238         movl    CNAME(int_to_apicintpin) + 12(%ecx), %ecx
239         testl   %edx, %edx
240         jz      1f
241
242         movl    %ecx, (%edx)            /* write the target register index */
243         movl    16(%edx), %eax          /* read the target register data */
244         andl    $~IOART_INTMASK, %eax   /* clear mask bit */
245         movl    %eax, 16(%edx)          /* write the APIC register data */
246 1:      
247         IMASK_UNLOCK                    /* exit critical reg */
248         popfl                           /* restore old state of EI flag */
249         ret
250
251 /*
252  * (soon to be) MP-safe function to set ONE INT mask bit.
253  * The passed arg is a 32bit u_int MASK.
254  * It clears the associated bit in _apic_imen.
255  * It clears the mask bit of the associated IO APIC register.
256  */
257 ENTRY(INTRDIS)
258         pushfl                          /* save state of EI flag */
259         cli                             /* prevent recursion */
260         IMASK_LOCK                      /* enter critical reg */
261
262         movl    8(%esp), %eax           /* mask into %eax */
263         bsfl    %eax, %ecx              /* get pin index */
264         btsl    %ecx, _apic_imen        /* update _apic_imen */
265
266         QUALIFY_MASK
267
268         shll    $4, %ecx
269         movl    CNAME(int_to_apicintpin) + 8(%ecx), %edx
270         movl    CNAME(int_to_apicintpin) + 12(%ecx), %ecx
271         testl   %edx, %edx
272         jz      1f
273
274         movl    %ecx, (%edx)            /* write the target register index */
275         movl    16(%edx), %eax          /* read the target register data */
276         orl     $IOART_INTMASK, %eax    /* set mask bit */
277         movl    %eax, 16(%edx)          /* write the APIC register data */
278 1:      
279         IMASK_UNLOCK                    /* exit critical reg */
280         popfl                           /* restore old state of EI flag */
281         ret
282
283
284 /******************************************************************************
285  *
286  */
287
288
289 /*
290  * void write_ioapic_mask(int apic, u_int mask); 
291  */
292
293 #define _INT_MASK       0x00010000
294 #define _PIN_MASK       0x00ffffff
295
296 #define _OLD_ESI          0(%esp)
297 #define _OLD_EBX          4(%esp)
298 #define _RETADDR          8(%esp)
299 #define _APIC            12(%esp)
300 #define _MASK            16(%esp)
301
302         ALIGN_TEXT
303 write_ioapic_mask:
304         pushl %ebx                      /* scratch */
305         pushl %esi                      /* scratch */
306
307         movl    _apic_imen, %ebx
308         xorl    _MASK, %ebx             /* %ebx = _apic_imen ^ mask */
309         andl    $_PIN_MASK, %ebx        /* %ebx = _apic_imen & 0x00ffffff */
310         jz      all_done                /* no change, return */
311
312         movl    _APIC, %esi             /* APIC # */
313         movl    _ioapic, %ecx
314         movl    (%ecx,%esi,4), %esi     /* %esi holds APIC base address */
315
316 next_loop:                              /* %ebx = diffs, %esi = APIC base */
317         bsfl    %ebx, %ecx              /* %ecx = index if 1st/next set bit */
318         jz      all_done
319
320         btrl    %ecx, %ebx              /* clear this bit in diffs */
321         leal    16(,%ecx,2), %edx       /* calculate register index */
322
323         movl    %edx, (%esi)            /* write the target register index */
324         movl    16(%esi), %eax          /* read the target register data */
325
326         btl     %ecx, _MASK             /* test for mask or unmask */
327         jnc     clear                   /* bit is clear */
328         orl     $_INT_MASK, %eax        /* set mask bit */
329         jmp     write
330 clear:  andl    $~_INT_MASK, %eax       /* clear mask bit */
331
332 write:  movl    %eax, 16(%esi)          /* write the APIC register data */
333
334         jmp     next_loop               /* try another pass */
335
336 all_done:
337         popl    %esi
338         popl    %ebx
339         ret
340
341 #undef _OLD_ESI
342 #undef _OLD_EBX
343 #undef _RETADDR
344 #undef _APIC
345 #undef _MASK
346
347 #undef _PIN_MASK
348 #undef _INT_MASK
349
350 #ifdef oldcode
351
352 _INTREN:
353         movl _apic_imen, %eax
354         notl %eax                       /* mask = ~mask */
355         andl _apic_imen, %eax           /* %eax = _apic_imen & ~mask */
356
357         pushl %eax                      /* new (future) _apic_imen value */
358         pushl $0                        /* APIC# arg */
359         call write_ioapic_mask          /* modify the APIC registers */
360
361         addl $4, %esp                   /* remove APIC# arg from stack */
362         popl _apic_imen                 /* _apic_imen |= mask */
363         ret
364
365 _INTRDIS:
366         movl _apic_imen, %eax
367         orl 4(%esp), %eax               /* %eax = _apic_imen | mask */
368
369         pushl %eax                      /* new (future) _apic_imen value */
370         pushl $0                        /* APIC# arg */
371         call write_ioapic_mask          /* modify the APIC registers */
372
373         addl $4, %esp                   /* remove APIC# arg from stack */
374         popl _apic_imen                 /* _apic_imen |= mask */
375         ret
376
377 #endif /* oldcode */
378
379
380 #ifdef ready
381
382 /*
383  * u_int read_io_apic_mask(int apic); 
384  */
385         ALIGN_TEXT
386 read_io_apic_mask:
387         ret
388
389 /*
390  * Set INT mask bit for each bit set in 'mask'.
391  * Ignore INT mask bit for all others.
392  *
393  * void set_io_apic_mask(apic, u_int32_t bits); 
394  */
395         ALIGN_TEXT
396 set_io_apic_mask:
397         ret
398
399 /*
400  * void set_ioapic_maskbit(int apic, int bit); 
401  */
402         ALIGN_TEXT
403 set_ioapic_maskbit:
404         ret
405
406 /*
407  * Clear INT mask bit for each bit set in 'mask'.
408  * Ignore INT mask bit for all others.
409  *
410  * void clr_io_apic_mask(int apic, u_int32_t bits); 
411  */
412         ALIGN_TEXT
413 clr_io_apic_mask:
414         ret
415
416 /*
417  * void clr_ioapic_maskbit(int apic, int bit); 
418  */
419         ALIGN_TEXT
420 clr_ioapic_maskbit:
421         ret
422
423 #endif /** ready */
424
425 /******************************************************************************
426  * 
427  */
428
429 /*
430  * u_int io_apic_write(int apic, int select);
431  */
432 ENTRY(io_apic_read)
433         movl    4(%esp), %ecx           /* APIC # */
434         movl    _ioapic, %eax
435         movl    (%eax,%ecx,4), %edx     /* APIC base register address */
436         movl    8(%esp), %eax           /* target register index */
437         movl    %eax, (%edx)            /* write the target register index */
438         movl    16(%edx), %eax          /* read the APIC register data */
439         ret                             /* %eax = register value */
440
441 /*
442  * void io_apic_write(int apic, int select, int value);
443  */
444 ENTRY(io_apic_write)
445         movl    4(%esp), %ecx           /* APIC # */
446         movl    _ioapic, %eax
447         movl    (%eax,%ecx,4), %edx     /* APIC base register address */
448         movl    8(%esp), %eax           /* target register index */
449         movl    %eax, (%edx)            /* write the target register index */
450         movl    12(%esp), %eax          /* target register value */
451         movl    %eax, 16(%edx)          /* write the APIC register data */
452         ret                             /* %eax = void */
453
454 /*
455  * Send an EOI to the local APIC.
456  */
457 ENTRY(apic_eoi)
458         movl    $0, _lapic+0xb0
459         ret