1/*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include "pico.h"
8#include "hardware/irq.h"
9#include "pico/asm_helper.S"
10
11pico_default_asm_setup
12
13#if !PICO_DISABLE_SHARED_IRQ_HANDLERS
14
15.data
16.align 2
17
18.global irq_handler_chain_slots
19
20.global irq_handler_chain_first_slot
21.global irq_handler_chain_remove_tail
22
23//
24// These Slots make up the code and structure of the handler chains; the only external information are the VTABLE entries
25// (obviously one set per core) and a free list head. Each individual handler chain starts with the VTABLE entry I
26// pointing at the address of slot S (with thumb bit set). Thus each slot which is part of a chain is executable.
27//
28// The execution jumps (via branch instruction) from one slot to the other, then jumps to the end of chain handler.
29// The entirety of the state needed to traverse the chain is contained within the slots of the chain, which is why
30// a VTABLE entry is all that is needed per chain (rather than requiring a separarte set of head pointers)
31//
32
33irq_handler_chain_slots:
34.set next_slot_number, 1
35.rept PICO_MAX_SHARED_IRQ_HANDLERS
36    // a slot is executable and is always 3 instructions long.
37#ifndef __riscv
38    .hword 0    // inst1 (either: ldr r0, [pc, #4]           or for the FIRST slot:  add r1, pc, #0                      )
39    .hword 0    // inst2 (        blx r0                                             b irq_handler_chain_first_slot      )
40
41    .hword 0    // inst3 (either: b next_slot                or for the LAST         pop {pc}                            )
42#else
43    .word  0    // inst1 (either: lui  ra, %hi(handler)      or for the FIRST slot:  jal t0, irq_handler_chain_first_slot)
44    .word  0    // inst2 (either: jalr ra. %lo(handler)(ra)                          .word handler                       )
45
46    .hword 0    // inst3 (either: j next_slot                or for the LAST slot:   cm.popret {ra}, 16                  )
47#endif
48
49    // next is a single byte index of next slot in chain (or -1 to end)
50.if next_slot_number == PICO_MAX_SHARED_IRQ_HANDLERS
51    .byte 0xff
52.else
53    .byte next_slot_number
54.endif
55    // next is the 8 bit unsigned priority
56    .byte 0x00
571:
58    // and finally the handler function pointer for Arm:
59#ifndef __riscv
60    .word 0x0000000
61#endif
62    .set next_slot_number, next_slot_number + 1
63.endr
64
65irq_handler_chain_first_slot:
66#ifndef __riscv
67    push {r0, lr}     // Save EXC_RETURN token, so `pop {r0, pc}` will return from interrupt
68                      // Note that r0 does not NEED to be saved, however we must maintain
69                      // an 8 byte stack alignment, and this is the cheapest way to do so
70    ldr  r0, [r1, #4] // Get `handler` field of irq_handler_chain_slot
71    adds r1, #1       // r1 points to `inst3` field of slot struct. Set Thumb bit on r1,
72    mov  lr, r1       // and copy to lr, so `inst3` is executed on return from handler
73    bx   r0           // Enter handler
74#else
75    .insn 0xb842      // cm.push {ra}, -16: Save ultimate return address
76    add ra, t0, 4     // Set up function call to return to offset 8 of the slot
77    lw t0, (t0)       // Load pointer from offset 4 of the slot
78    jr t0             // Call it, with our calculated return address
79#endif
80
81irq_handler_chain_remove_tail:
82#ifndef __riscv
83    mov  r0, lr       // Get start of struct. This function was called by a bl at offset +4,
84    subs r0, #9       // so lr points to offset +8. Note also lr has its Thumb bit set!
85    ldr  r1, =irq_add_tail_to_free_list
86    blx  r1
87    pop  {r0, pc}     // Top of stack is EXC_RETURN
88#else
89    add a0, ra, -10   // Expect to be called with a 16-bit jal, at 8-byte offset in the slot.
90    call irq_add_tail_to_free_list
91    .insn 0xbe42      // cm.popret {ra}, 16
92#endif
93
94#endif
95