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