|
| 1 | +/* |
| 2 | + * Copyright (c) 2023 Advanced Micro Devices, Inc. (AMD) |
| 3 | + * Copyright (c) 2023 Alp Sayin <alpsayin@gmail.com> |
| 4 | + * |
| 5 | + * SPDX-License-Identifier: Apache-2.0 |
| 6 | + */ |
| 7 | + |
| 8 | + |
| 9 | +#include <zephyr/arch/cpu.h> |
| 10 | +#include <zephyr/irq.h> |
| 11 | +#include <zephyr/kernel.h> |
| 12 | +#include <zephyr/kernel_structs.h> |
| 13 | +#include <zephyr/logging/log.h> |
| 14 | +#include <zephyr/sw_isr_table.h> |
| 15 | +#include <zephyr/tracing/tracing.h> |
| 16 | + |
| 17 | +#include <ksched.h> |
| 18 | +#include <kswap.h> |
| 19 | +#include <microblaze/mb_interface.h> |
| 20 | + |
| 21 | +LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL); |
| 22 | + |
| 23 | +static uint32_t emulated_irq_pending; |
| 24 | + |
| 25 | +FUNC_NORETURN void z_irq_spurious(const void *param) |
| 26 | +{ |
| 27 | + LOG_ERR("Spurious interrupt detected!\n" |
| 28 | + "\tmsr: %x\n" |
| 29 | + "\tesr: %x\n" |
| 30 | + "\tear: %x\n" |
| 31 | + "\tedr: %x\n" |
| 32 | + "\tparam: %x\n", |
| 33 | + (unsigned int)mfmsr(), (unsigned int)mfesr(), (unsigned int)mfear(), |
| 34 | + (unsigned int)mfedr(), (unsigned int)param); |
| 35 | + |
| 36 | + z_microblaze_fatal_error(K_ERR_SPURIOUS_IRQ, NULL); |
| 37 | + CODE_UNREACHABLE; |
| 38 | +} |
| 39 | + |
| 40 | +/** |
| 41 | + * @brief Returns if IE bit is enabled |
| 42 | + * Defined weak so soc/boards can override it. |
| 43 | + */ |
| 44 | +__weak int arch_irq_is_enabled(unsigned int irq) |
| 45 | +{ |
| 46 | + ARG_UNUSED(irq); |
| 47 | + return mfmsr() & MSR_IE_MASK; |
| 48 | +} |
| 49 | + |
| 50 | +/** |
| 51 | + * @brief Simply unlocks all IRQs. |
| 52 | + * Defined weak so soc/boards can override it. |
| 53 | + */ |
| 54 | +__weak void arch_irq_enable(unsigned int irq) |
| 55 | +{ |
| 56 | + ARG_UNUSED(irq); |
| 57 | + arch_irq_unlock(1); |
| 58 | +}; |
| 59 | + |
| 60 | +/** |
| 61 | + * @brief Simply locks all IRQs. |
| 62 | + * Defined weak so soc/boards can override it. |
| 63 | + */ |
| 64 | +__weak void arch_irq_disable(unsigned int irq) |
| 65 | +{ |
| 66 | + ARG_UNUSED(irq); |
| 67 | + arch_irq_lock(); |
| 68 | +}; |
| 69 | + |
| 70 | +/** |
| 71 | + * @brief Returns the currently pending interrupts. |
| 72 | + * This function should be overridden if an AXI interrupt |
| 73 | + * controller is placed inside the SoC. |
| 74 | + * Since there's no way to tell if a barebones MicroBlaze is |
| 75 | + * pending on an interrupt signal, this function will return 1 on first call, |
| 76 | + * and returns 0 on second call, which is enough to |
| 77 | + * make the _enter_irq break its while(ipending) loop. |
| 78 | + * This is a logically correct hack to make _enter_irq below work for |
| 79 | + * barebones MicroBlaze without introducing extra logic to the _enter_irq logic. |
| 80 | + * Obviously, this function shouldn't be used for generic purposes and is merely |
| 81 | + * a weak stub for soc/boards to override with more meaningful implementations. |
| 82 | + * |
| 83 | + * @return Pending IRQ bitmask. Pending IRQs will have their bitfield set to 1. 0 if no interrupt is |
| 84 | + * pending. |
| 85 | + */ |
| 86 | +__weak uint32_t arch_irq_pending(void) |
| 87 | +{ |
| 88 | + static uint32_t call_count; |
| 89 | + |
| 90 | + /* xor with 1 should simply invert the value */ |
| 91 | + call_count ^= 1; |
| 92 | + return call_count; |
| 93 | +}; |
| 94 | + |
| 95 | +__weak uint32_t arch_irq_pending_vector(uint32_t irq_pending) |
| 96 | +{ |
| 97 | + return find_lsb_set(irq_pending) - 1; |
| 98 | +} |
| 99 | + |
| 100 | +/** |
| 101 | + * @brief * Even in the presence of an interrupt controller, once the real mode |
| 102 | + * is enabled, there is no way to emulate hw interrupts. So this routine provides |
| 103 | + * a software "triggering" capability to MicroBlaze. This routine MUST be called |
| 104 | + * either with IRQs locked or interrupts disabled otherwise a real IRQ could fire. |
| 105 | + * Also see emulate_isr.h |
| 106 | + * |
| 107 | + * @param irq IRQ number to enable. |
| 108 | + * @return uint32_t returns the final emulated irq_pending mask. |
| 109 | + */ |
| 110 | +ALWAYS_INLINE uint32_t arch_irq_set_emulated_pending(uint32_t irq) |
| 111 | +{ |
| 112 | + return emulated_irq_pending |= BIT(irq); |
| 113 | +} |
| 114 | + |
| 115 | +/** |
| 116 | + * @brief Called by _interrupt_handler in isr.S |
| 117 | + */ |
| 118 | +void _enter_irq(void) |
| 119 | +{ |
| 120 | + _kernel.cpus[0].nested++; |
| 121 | + |
| 122 | +#ifdef CONFIG_IRQ_OFFLOAD |
| 123 | + z_irq_do_offload(); |
| 124 | +#endif |
| 125 | + |
| 126 | + uint32_t real_irq_pending = 0; |
| 127 | + |
| 128 | + while (true) { |
| 129 | + struct _isr_table_entry *ite; |
| 130 | + |
| 131 | + real_irq_pending = arch_irq_pending(); |
| 132 | + |
| 133 | + if (real_irq_pending == 0 && emulated_irq_pending == 0) { |
| 134 | + break; |
| 135 | + } |
| 136 | + |
| 137 | +#ifdef CONFIG_TRACING_ISR |
| 138 | + sys_trace_isr_enter(); |
| 139 | +#endif |
| 140 | + |
| 141 | + /* From pg099 AXI Interrupt Controller (INTC) product guide: |
| 142 | + * The least significant bit (LSB, in this case bit 0) has the highest priority. |
| 143 | + */ |
| 144 | + const uint32_t index = (real_irq_pending != 0) |
| 145 | + ? arch_irq_pending_vector(real_irq_pending) |
| 146 | + : find_lsb_set(emulated_irq_pending) - 1; |
| 147 | + |
| 148 | + ite = &_sw_isr_table[index]; |
| 149 | + |
| 150 | + ite->isr(ite->arg); |
| 151 | + |
| 152 | + /* In this implementation it's the ISR's responsibility to clear irq flags. |
| 153 | + * But _enter_irq does clear the emulated IRQs automatically since this is a port |
| 154 | + * provided functionality and also needed to pass unit tests without altering tests. |
| 155 | + */ |
| 156 | + emulated_irq_pending &= ~BIT(index); |
| 157 | + |
| 158 | +#ifdef CONFIG_TRACING_ISR |
| 159 | + sys_trace_isr_exit(); |
| 160 | +#endif |
| 161 | + } |
| 162 | + |
| 163 | + _kernel.cpus[0].nested--; |
| 164 | +#ifdef CONFIG_STACK_SENTINEL |
| 165 | + z_check_stack_sentinel(); |
| 166 | +#endif |
| 167 | +} |
| 168 | + |
| 169 | +#ifdef CONFIG_DYNAMIC_INTERRUPTS |
| 170 | +int arch_irq_connect_dynamic(unsigned int irq, unsigned int priority, |
| 171 | + void (*routine)(const void *parameter), const void *parameter, |
| 172 | + uint32_t flags) |
| 173 | +{ |
| 174 | + ARG_UNUSED(flags); |
| 175 | + ARG_UNUSED(priority); |
| 176 | + |
| 177 | + z_isr_install(irq, routine, parameter); |
| 178 | + return irq; |
| 179 | +} |
| 180 | +#endif /* CONFIG_DYNAMIC_INTERRUPTS */ |
0 commit comments