1 /*
2  * Copyright (c) 2022 Carlo Caione <ccaione@baylibre.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief RISC-V public interrupt handling
10  *
11  * RISC-V-specific kernel interrupt handling interface.
12  */
13 
14 #ifndef ZEPHYR_INCLUDE_ARCH_RISCV_IRQ_H_
15 #define ZEPHYR_INCLUDE_ARCH_RISCV_IRQ_H_
16 
17 #ifdef __cplusplus
18 extern "C" {
19 #endif
20 
21 #include <zephyr/sys/util_macro.h>
22 
23 #ifndef _ASMLANGUAGE
24 #include <zephyr/irq.h>
25 #include <zephyr/sw_isr_table.h>
26 #include <stdbool.h>
27 #endif /* !_ASMLANGUAGE */
28 
29 /* Exceptions 0-15 (MCAUSE interrupt=0) */
30 
31 /* Environment Call from U-mode */
32 #define RISCV_EXC_ECALLU 8
33 /** Environment Call from M-mode */
34 #define RISCV_EXC_ECALLM 11
35 
36 /* IRQs 0-15 (MCAUSE interrupt=1) */
37 
38 /** Machine Software Interrupt */
39 #define RISCV_IRQ_MSOFT 3
40 /** Machine External Interrupt */
41 #define RISCV_IRQ_MEXT  11
42 
43 #ifdef CONFIG_64BIT
44 #define RISCV_MCAUSE_IRQ_POS          63U
45 #define RISCV_MCAUSE_IRQ_BIT          BIT64(RISCV_MCAUSE_IRQ_POS)
46 #else
47 #define RISCV_MCAUSE_IRQ_POS          31U
48 #define RISCV_MCAUSE_IRQ_BIT          BIT(RISCV_MCAUSE_IRQ_POS)
49 #endif
50 
51 #ifndef _ASMLANGUAGE
52 
53 extern void arch_irq_enable(unsigned int irq);
54 extern void arch_irq_disable(unsigned int irq);
55 extern int arch_irq_is_enabled(unsigned int irq);
56 
57 #if defined(CONFIG_RISCV_HAS_PLIC) || defined(CONFIG_RISCV_HAS_CLIC)
58 extern void z_riscv_irq_priority_set(unsigned int irq,
59 				     unsigned int prio,
60 				     uint32_t flags);
61 #else
62 #define z_riscv_irq_priority_set(i, p, f) /* Nothing */
63 #endif /* CONFIG_RISCV_HAS_PLIC || CONFIG_RISCV_HAS_CLIC */
64 
65 #ifdef CONFIG_RISCV_HAS_CLIC
66 extern void z_riscv_irq_vector_set(unsigned int irq);
67 #else
68 #define z_riscv_irq_vector_set(i) /* Nothing */
69 #endif /* CONFIG_RISCV_HAS_CLIC */
70 
71 #define ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p) \
72 { \
73 	Z_ISR_DECLARE(irq_p + CONFIG_RISCV_RESERVED_IRQ_ISR_TABLES_OFFSET, \
74 		      0, isr_p, isr_param_p); \
75 	z_riscv_irq_priority_set(irq_p, priority_p, flags_p); \
76 }
77 
78 #define ARCH_IRQ_DIRECT_CONNECT(irq_p, priority_p, isr_p, flags_p) \
79 { \
80 	Z_ISR_DECLARE_DIRECT(irq_p + CONFIG_RISCV_RESERVED_IRQ_ISR_TABLES_OFFSET, \
81 		      ISR_FLAG_DIRECT, isr_p); \
82 	z_riscv_irq_priority_set(irq_p, priority_p, flags_p); \
83 	z_riscv_irq_vector_set(irq_p); \
84 }
85 
86 #define ARCH_ISR_DIRECT_HEADER() arch_isr_direct_header()
87 #define ARCH_ISR_DIRECT_FOOTER(swap) arch_isr_direct_footer(swap)
88 
89 #ifdef CONFIG_TRACING_ISR
90 extern void sys_trace_isr_enter(void);
91 extern void sys_trace_isr_exit(void);
92 #endif
93 
arch_isr_direct_header(void)94 static inline void arch_isr_direct_header(void)
95 {
96 #ifdef CONFIG_TRACING_ISR
97 	sys_trace_isr_enter();
98 #endif
99 	/* We need to increment this so that arch_is_in_isr() keeps working */
100 	++(arch_curr_cpu()->nested);
101 }
102 
103 extern void __soc_handle_irq(unsigned long mcause);
104 
arch_isr_direct_footer(int swap)105 static inline void arch_isr_direct_footer(int swap)
106 {
107 	ARG_UNUSED(swap);
108 	unsigned long mcause;
109 
110 	/* Get the IRQ number */
111 	__asm__ volatile("csrr %0, mcause" : "=r" (mcause));
112 	mcause &= CONFIG_RISCV_MCAUSE_EXCEPTION_MASK;
113 
114 	/* Clear the pending IRQ */
115 	__soc_handle_irq(mcause);
116 
117 	/* We are not in the ISR anymore */
118 	--(arch_curr_cpu()->nested);
119 
120 #ifdef CONFIG_TRACING_ISR
121 	sys_trace_isr_exit();
122 #endif
123 }
124 
125 /*
126  * TODO: Add support for rescheduling
127  */
128 #define ARCH_ISR_DIRECT_DECLARE(name) \
129 	static inline int name##_body(void); \
130 	__attribute__ ((interrupt)) void name(void) \
131 	{ \
132 		ISR_DIRECT_HEADER(); \
133 		name##_body(); \
134 		ISR_DIRECT_FOOTER(0); \
135 	} \
136 	static inline int name##_body(void)
137 
138 #endif /* _ASMLANGUAGE */
139 
140 #ifdef __cplusplus
141 }
142 #endif
143 
144 #endif /* ZEPHYR_INCLUDE_ARCH_RISCV_IRQ_H_ */
145