Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
/*	$NetBSD: iomd_irq.S,v 1.18 2020/11/21 19:52:56 skrll Exp $	*/

/*
 * Copyright (c) 1994-1998 Mark Brinicombe.
 * Copyright (c) 1994 Brini.
 * All rights reserved.
 *
 * This code is derived from software written for Brini by Mark Brinicombe
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Mark Brinicombe
 *	for the NetBSD Project.
 * 4. The name of the company nor the name of the author may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Low level irq and fiq handlers
 *
 * Created      : 27/09/94
 */

#include "opt_irqstats.h"

#include "assym.h"
#include <arm/asm.h>
#include <arm/locore.h>
#include <arm/iomd/iomdreg.h>

	.text
	.align	0
/*
 * ffs table used for servicing irq's quickly must be here otherwise adr can't
 * reach it
 * The algorithm for ffs was devised by D. Seal and posted to
 * comp.sys.arm on 16 Feb 1994.
 */
.type Lirq_ffs_table, _ASM_TYPE_OBJECT;
Lirq_ffs_table:
/* same as ffs table but all nums are -1 from that */
/*               0   1   2   3   4   5   6   7           */
	.byte	 0,  0,  1, 12,  2,  6,  0, 13  /*  0- 7 */
	.byte	 3,  0,  7,  0,  0,  0,  0, 14  /*  8-15 */
	.byte	10,  4,  0,  0,  8,  0,  0, 25  /* 16-23 */
	.byte	 0,  0,  0,  0,  0, 21, 27, 15  /* 24-31 */
	.byte	31, 11,  5,  0,  0,  0,  0,  0	/* 32-39 */
	.byte	 9,  0,  0, 24,  0,  0, 20, 26  /* 40-47 */
	.byte	30,  0,  0,  0,  0, 23,  0, 19  /* 48-55 */
	.byte   29,  0, 22, 18, 28, 17, 16,  0  /* 56-63 */

/*
 *
 * irq_entry
 *
 * Main entry point for the IRQ vector
 *
 * This function reads the irq request bits in the IOMD registers
 * IRQRQA, IRQRQB and DMARQ
 * It then calls an installed handler for each bit that is set.
 * The function stray_irqhandler is called if a handler is not defined
 * for a particular interrupt.
 * If a interrupt handler is found then it is called with r0 containing
 * the argument defined in the handler structure. If the field ih_arg
 * is zero then a pointer to the IRQ frame on the stack is passed instead.
 */

Lcurrent_spl_level:
	.word	_C_LABEL(cpu_info_store) + CI_CPL

Ldisabled_mask:
	.word	_C_LABEL(disabled_mask)

Lspl_masks:
	.word	_C_LABEL(spl_masks)

LOCK_CAS_CHECK_LOCALS

AST_ALIGNMENT_FAULT_LOCALS

/*
 * Register usage
 *
 *  r4  - Address of cpu_info
 *  r5  - Address of curlwp
 *  r6  - Address of current handler
 *  r7  - Pointer to handler pointer list
 *  r8  - Current IRQ requests.
 *  r9  - scratch
 *  r10 - Base address of IOMD
 *  r11 - IRQ requests still to service.
 */

Liomd_base:
	.word	_C_LABEL(iomd_base)

Larm7500_ioc_found:
	.word	_C_LABEL(arm7500_ioc_found)

ASENTRY_NP(irq_entry)
	sub	lr, lr, #0x00000004	/* Adjust the lr */

	PUSHFRAMEINSVC			/* Push an interrupt frame */
	ENABLE_ALIGNMENT_FAULTS		/* puts cur{cpu,lwp} in r4/r5 */

	str	r7, [sp, #TF_FILL]	/* save r7 */

	/* Load r8 with the IOMD interrupt requests */

	ldr	r10, Liomd_base
 	ldr	r10, [r10]			/* Point to the IOMD */
	ldrb	r8, [r10, #(IOMD_IRQRQA << 2)]	/* Get IRQ request A */
	ldrb	r9, [r10, #(IOMD_IRQRQB << 2)]	/* Get IRQ request B */
	orr	r8, r8, r9, lsl #8

	ldr	r9, Larm7500_ioc_found
	ldr	r9, [r9]			/* get the flag      */
	cmp	r9, #0
	beq	skip_extended_IRQs_reading

	/* ARM 7500 only */
	ldrb	r9, [r10, #(IOMD_IRQRQC << 2)]	/* Get IRQ request C */
	orr	r8, r8, r9, lsl #16
	ldrb	r9, [r10, #(IOMD_IRQRQD << 2)]	/* Get IRQ request D */
	orr	r8, r8, r9, lsl #24
	ldrb	r9, [r10, #(IOMD_DMARQ << 2)]	/* Get DMA Request */
	tst	r9, #0x10
	orrne	r8, r8, r9, lsl #27
	b	irq_entry_continue

skip_extended_IRQs_reading:
	/* non ARM7500 machines */
	ldrb	r9, [r10, #(IOMD_DMARQ << 2)]	/* Get DMA Request */
	orr	r8, r8, r9, lsl #16
irq_entry_continue:

	and	r0, r8, #0x7d		/* Clear IOMD IRQA bits */
	strb	r0, [r10, #(IOMD_IRQRQA << 2)]

	/*
	 * Note that we have entered the IRQ handler.
	 * We are in SVC mode so we cannot use the processor mode
	 * to determine if we are in an IRQ. Instead we will count the
	 * each time the interrupt handler is nested.
	 */

	ldr	r0, [r4, #CI_INTR_DEPTH]
	add	r0, r0, #1
	str	r0, [r4, #CI_INTR_DEPTH]

	/* Block the current requested interrupts */
	ldr	r1, Ldisabled_mask
	ldr	r0, [r1]
	stmfd	sp!, {r0}
	orr	r0, r0, r8

	/*
 	 * Need to block all interrupts at the IPL or lower for
	 * all asserted interrupts.
	 * This basically emulates hardware interrupt priority levels.
	 * Means we need to go through the interrupt mask and for
	 * every asserted interrupt we need to mask out all other
	 * interrupts at the same or lower IPL.
	 * If only we could wait until the main loop but we need to sort
	 * this out first so interrupts can be re-enabled.
	 *
	 * This would benefit from a special ffs type routine
	 */

	mov	r9, #(NIPL - 1)
	ldr	r7, Lspl_masks

Lfind_highest_ipl:
	ldr	r2, [r7, r9, lsl #2]
	tst	r8, r2
	subeq	r9, r9, #1
	beq	Lfind_highest_ipl

	/* r9 = SPL level of highest priority interrupt */
	add	r9, r9, #1
	ldr	r2, [r7, r9, lsl #2]
	mvn	r2, r2
	orr	r0, r0, r2

	str	r0, [r1]

	ldr	r0, [r4, #CI_CPL]
	str	r9, [r4, #CI_CPL]
	stmfd	sp!, {r0}

	/* Update the IOMD irq masks */
	bl	_C_LABEL(irq_setmasks)

        mrs     r0, cpsr		/* Enable IRQ's */
	bic	r0, r0, #I32_bit
	msr	cpsr_all, r0

	/*
	 * take a copy of the IRQ request so that we can strip bits out of it
	 * note that we only use 24 bits with iomd2 chips
	 */
	ldr	r7, Larm7500_ioc_found
	ldr	r7, [r7]			/* get the flag      */
	cmp	r7, #0
	movne	r11, r8				/* ARM7500  -> copy all bits   */
	biceq	r11, r8, #0xff000000		/* !ARM7500 -> only use 24 bit */

	/* ffs routine to find first irq to service */
	/* standard trick to isolate bottom bit in a0 or 0 if a0 = 0 on entry */
	rsb	r7, r11, #0
	ands	r10, r11, r7

	/*
	 * now r10 has at most 1 set bit, call this X
	 * if X = 0, branch to exit code
	 */
	beq	exitirq
irqloop:
	ldr	r6, Lirqhandlers
	adr	r7, Lirq_ffs_table
	/*
	 * at this point:
	 * 	r6 = address of irq handlers table
	 *	r7 = address of ffs table
	 *	r8 = irq request
	 *	r10 = bit of irq to be serviced
	 *	r11 = bitmask of IRQ's to service
	 */
	/* find the set bit */
	orr	r9, r10, r10, lsl #4	/* X * 0x11 */
	orr	r9, r9, r9, lsl #6	/* X * 0x451 */
	rsb	r9, r9, r9, lsl #16	/* X * 0x0450fbaf */
	/* fetch the bit number */
	ldrb	r9, [r7, r9, lsr #26 ]

	/*
	 * r9 = irq to service
	 */

	/* apologies for the dogs dinner of code here, but it's in an attempt
	 * to minimise stalling on SA's, hence lots of things happen here:
	 *	- getting address of handler, if it doesn't exist we call
	 *	  stray_irqhandler this is assumed to be rare so we don't
	 *	  care about performance for it
	 *	- statinfo is updated
	 *	- unsetting of the irq bit in r11
	 *	- irq stats (if enabled) also get put in the mix
	 */
	ldr	r6, [r6, r9, lsl #2]	/* Get address of first handler structure */

	teq	r6, #0x00000000		/* Do we have a handler */
	moveq	r0, r8			/* IRQ requests as arg 0 */
	adreq	lr, nextirq		/* return Address */
	beq	_C_LABEL(stray_irqhandler) /* call special handler */

	/* stat info C */
	ldr	r1, [r4, #(CI_CC_NINTR)] /* Stat info B */
	ldr	r2, [r4, #(CI_CC_NINTR+4)]
#ifdef _ARMEL
	adds	r1, r1, #0x00000001
	adc	r2, r2, #0x00000000
#else
	adds	r2, r2, #0x00000001
	adc	r1, r1, #0x00000000
#endif
	str	r1, [r4, #(CI_CC_NINTR)]
	str	r2, [r4, #(CI_CC_NINTR+4)]

#ifdef IRQSTATS
	ldr	r2, Lintrcnt
	ldr	r3, [r6, #(IH_NUM)]
	ldr	r3, [r2, r3, lsl #2]!
#endif
	bic	r11, r11, r10		/* clear the IRQ bit */

#ifdef IRQSTATS
	add	r3, r3, #0x00000001
	str	r3, [r2]
#endif	/* IRQSTATS */

irqchainloop:
	ldr	r0, [r6, #(IH_ARG)]	/* Get argument pointer */
	teq	r0, #0x00000000		/* If arg is zero pass stack frame */
	addeq	r0, sp, #8		/* ... stack frame [XXX needs care] */

	mov	lr, pc			/* return address */
	ldr	pc, [r6, #(IH_FUNC)]	/* Call handler */

	ldr	r6, [r6, #(IH_NEXT)]	/* fetch next handler */

	teq	r0, #0x00000001		/* Was the irq serviced ? */

	/* if it was it'll just fall through this: */
	teqne	r6, #0x00000000
	bne	irqchainloop
nextirq:
	/* Check for next irq */
	rsb	r7, r11, #0
	ands	r10, r11, r7
	/* check if there are anymore irq's to service */
	bne 	irqloop

exitirq:
	ldmfd	sp!, {r2, r3}
	ldr	r0, Ldisabled_mask
	str	r2, [r4, #CI_CPL]
	str	r3, [r0]

	bl	_C_LABEL(irq_setmasks)

	/* Kill IRQ's in preparation for exit */
        mrs     r0, cpsr
        orr     r0, r0, #(I32_bit)
        msr     cpsr_all, r0

	/* Decrement the nest count */
	ldr	r0, [r4, #CI_INTR_DEPTH]
	sub	r0, r0, #1
	str	r0, [r4, #CI_INTR_DEPTH]

	ldr	r7, [sp, #TF_FILL]		/* restore r7 */
	LOCK_CAS_CHECK

	DO_AST_AND_RESTORE_ALIGNMENT_FAULTS
	PULLFRAMEFROMSVCANDEXIT

	/* NOT REACHED */
	b	. - 8

Lcurrent_mask:
	.word	_C_LABEL(current_mask)	/* irq's that are usable */

ENTRY(irq_setmasks)
	/* Disable interrupts */
	mrs	r3, cpsr
	orr	r1, r3,  #(I32_bit)
	msr	cpsr_all, r1

	/* Calculate IOMD interrupt mask */
	ldr	r1, Lcurrent_mask	/* All the enabled interrupts */
	ldr	r1, [r1]
	ldr	r0, Lspl_masks		/* Block due to current spl level */
	ldr	r2, Lcurrent_spl_level
	ldr	r2, [r2]
	ldr	r2, [r0, r2, lsl #2]
	and	r1, r1, r2
	ldr	r2, Ldisabled_mask	/* Block due to active interrupts */
	ldr	r2, [r2]
	bic	r1, r1, r2

	ldr	r0, Liomd_base
 	ldr	r0, [r0]			/* Point to the IOMD */
	strb	r1, [r0, #(IOMD_IRQMSKA << 2)]	/* Set IRQ mask A */
	mov	r1, r1, lsr #8
	strb	r1, [r0, #(IOMD_IRQMSKB << 2)]	/* Set IRQ mask B */
	mov	r1, r1, lsr #8

	ldr	r2, Larm7500_ioc_found
	ldr	r2, [r2]
	cmp	r2, #0
	beq	skip_setting_extended_DMA_mask

	/* only for ARM7500's */
	strb	r1, [r0, #(IOMD_IRQMSKC << 2)]
	mov	r1, r1, lsr #8
	and	r2, r1, #0xef
	strb	r2, [r0, #(IOMD_IRQMSKD << 2)]
	mov	r1, r1, lsr #3
	and	r2, r1, #0x10
	strb	r2, [r0, #(IOMD_DMAMSK << 2)]	/* Set DMA mask */
	b	continue_setting_masks

skip_setting_extended_DMA_mask:
	/* non ARM7500's */
	strb	r1, [r0, #(IOMD_DMAMSK << 2)]	/* Set DMA mask */

continue_setting_masks:

	/* Restore old cpsr and exit */
	msr	cpsr_all, r3
	mov	pc, lr

Lintrcnt:
	.word	_C_LABEL(intrcnt)


Lirqhandlers:
	.word	_C_LABEL(irqhandlers)	/* Pointer to array of irqhandlers */

#ifdef IRQSTATS
/* These symbols are used by vmstat */

	.section .rodata

	.global	_C_LABEL(_intrnames)
_C_LABEL(_intrnames):
	.word	_C_LABEL(intrnames)

        .globl  _C_LABEL(intrnames), _C_LABEL(eintrnames), _C_LABEL(intrcnt), _C_LABEL(sintrcnt), _C_LABEL(eintrcnt)
_C_LABEL(intrnames):
	.asciz	"interrupt  0 "
	.asciz	"interrupt  1 "	/* reserved0 */
	.asciz	"interrupt  2 "
	.asciz	"interrupt  3 "
	.asciz	"interrupt  4 "
	.asciz	"interrupt  5 "
	.asciz	"interrupt  6 "
	.asciz	"interrupt  7 "	/* reserved1 */
	.asciz	"interrupt  8 " /* reserved2 */
	.asciz	"interrupt  9 "
	.asciz	"interrupt 10 "
	.asciz	"interrupt 11 "
	.asciz	"interrupt 12 "
	.asciz	"interrupt 13 "
	.asciz	"interrupt 14 "
	.asciz	"interrupt 15 "
	.asciz	"dma channel 0"
	.asciz	"dma channel 1"
	.asciz	"dma channel 2"
	.asciz	"dma channel 3"
	.asciz	"interrupt 20 "
	.asciz	"interrupt 21 "
	.asciz	"reserved 3   "
	.asciz	"reserved 4   "
	.asciz	"exp card 0   "
	.asciz	"exp card 1   "
	.asciz	"exp card 2   "
	.asciz	"exp card 3   "
	.asciz	"exp card 4   "
	.asciz	"exp card 5   "
	.asciz	"exp card 6   "
	.asciz	"exp card 7   "

_C_LABEL(sintrnames):
	.asciz	"softclock    "
	.asciz	"softnet      "
	.asciz	"softserial   "
	.asciz	"softintr  3  "
	.asciz	"softintr  4  "
	.asciz	"softintr  5  "
	.asciz	"softintr  6  "
	.asciz	"softintr  7   "
	.asciz	"softintr  8  "
	.asciz	"softintr  9  "
	.asciz	"softintr 10  "
	.asciz	"softintr 11  "
	.asciz	"softintr 12  "
	.asciz	"softintr 13  "
	.asciz	"softintr 14  "
	.asciz	"softintr 15  "
	.asciz	"softintr 16  "
	.asciz	"softintr 17  "
	.asciz	"softintr 18  "
	.asciz	"softintr 19  "
	.asciz	"softintr 20  "
	.asciz	"softintr 21  "
	.asciz	"softintr 22  "
	.asciz	"softintr 23  "
	.asciz	"softintr 24  "
	.asciz	"softintr 25  "
	.asciz	"softintr 26  "
	.asciz	"softintr 27  "
	.asciz	"softintr 28  "
	.asciz	"softintr 29  "
	.asciz	"softintr 30  "
	.asciz	"softintr 31  "
_C_LABEL(eintrnames):

	.bss
	.align	0
_C_LABEL(intrcnt):
	.space	32*4	/* XXX Should be linked to number of interrupts */

_C_LABEL(sintrcnt):
	.space	32*4	/* XXX Should be linked to number of interrupts */
_C_LABEL(eintrcnt):

#else	/* IRQSTATS */
	/* Dummy entries to keep vmstat happy */

	.section .rodata
        .globl  _C_LABEL(intrnames), _C_LABEL(eintrnames), _C_LABEL(intrcnt), _C_LABEL(eintrcnt)
_C_LABEL(intrnames):
	.long	0
_C_LABEL(eintrnames):

_C_LABEL(intrcnt):
	.long	0
_C_LABEL(eintrcnt):
#endif	/* IRQSTATS */