1 /*
2  * Copyright (c) 2017 Oticon A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * For all purposes, Zephyr threads see a CPU running at an infinitely high
9  * clock.
10  *
11  * Therefore, the code will always run until completion after each interrupt,
12  * after which arch_cpu_idle() will be called releasing the execution back to
13  * the HW models.
14  *
15  * The HW models raising an interrupt will "awake the cpu" by calling
16  * posix_interrupt_raised() which will transfer control to the irq handler,
17  * which will run inside SW/Zephyr context. After which a arch_swap() to
18  * whatever Zephyr thread may follow.  Again, once Zephyr is done, control is
19  * given back to the HW models.
20  *
21  * The Zephyr OS+APP code and the HW models are gated by a mutex +
22  * condition as there is no reason to let the zephyr threads run while the
23  * HW models run or vice versa
24  *
25  */
26 
27 #include <zephyr/arch/posix/posix_soc_if.h>
28 #include "posix_soc.h"
29 #include "posix_board_if.h"
30 #include "posix_core.h"
31 #include "posix_arch_internal.h"
32 #include "kernel_internal.h"
33 #include "soc.h"
34 #include "nce_if.h"
35 
36 static void *nce_st;
37 
posix_is_cpu_running(void)38 int posix_is_cpu_running(void)
39 {
40 	return nce_is_cpu_running(nce_st);
41 }
42 
43 /**
44  * Helper function which changes the status of the CPU (halted or running)
45  * and waits until somebody else changes it to the opposite
46  *
47  * Both HW and SW threads will use this function to transfer control to the
48  * other side.
49  *
50  * This is how the idle thread halts the CPU and gets halted until the HW models
51  * raise a new interrupt; and how the HW models awake the CPU, and wait for it
52  * to complete and go to idle.
53  */
posix_change_cpu_state_and_wait(bool halted)54 void posix_change_cpu_state_and_wait(bool halted)
55 {
56 	if (halted) {
57 		nce_halt_cpu(nce_st);
58 	} else {
59 		nce_wake_cpu(nce_st);
60 	}
61 }
62 
63 /**
64  * HW models shall call this function to "awake the CPU"
65  * when they are raising an interrupt
66  */
posix_interrupt_raised(void)67 void posix_interrupt_raised(void)
68 {
69 	/* We change the CPU to running state (we awake it), and block this
70 	 * thread until the CPU is halted again
71 	 */
72 	nce_wake_cpu(nce_st);
73 }
74 
75 
76 /**
77  * Normally called from arch_cpu_idle():
78  *   the idle loop will call this function to set the CPU to "sleep".
79  * Others may also call this function with care. The CPU will be set to sleep
80  * until some interrupt awakes it.
81  * Interrupts should be enabled before calling.
82  */
posix_halt_cpu(void)83 void posix_halt_cpu(void)
84 {
85 	/*
86 	 * We set the CPU in the halted state (this blocks this pthread
87 	 * until the CPU is awoken again by the HW models)
88 	 */
89 	nce_halt_cpu(nce_st);
90 
91 	/* We are awoken, normally that means some interrupt has just come
92 	 * => let the "irq handler" check if/what interrupt was raised
93 	 * and call the appropriate irq handler.
94 	 *
95 	 * Note that, the interrupt handling may trigger a arch_swap() to
96 	 * another Zephyr thread. When posix_irq_handler() returns, the Zephyr
97 	 * kernel has swapped back to this thread again
98 	 */
99 	posix_irq_handler();
100 
101 	/*
102 	 * And we go back to whatever Zephyr thread called us.
103 	 */
104 }
105 
106 
107 /**
108  * Implementation of arch_cpu_atomic_idle() for this SOC
109  */
posix_atomic_halt_cpu(unsigned int imask)110 void posix_atomic_halt_cpu(unsigned int imask)
111 {
112 	posix_irq_full_unlock();
113 	posix_halt_cpu();
114 	posix_irq_unlock(imask);
115 }
116 
117 /**
118  * The HW models will call this function to "boot" the CPU
119  * == spawn the Zephyr init thread, which will then spawn
120  * anything it wants, and run until the CPU is set back to idle again
121  */
posix_boot_cpu(void)122 void posix_boot_cpu(void)
123 {
124 	nce_st = nce_init();
125 	posix_arch_init();
126 	nce_boot_cpu(nce_st, z_cstart);
127 }
128 
129 /**
130  * Clean up all memory allocated by the SOC and POSIX core
131  *
132  * This function can be called from both HW and SW threads
133  */
posix_soc_clean_up(void)134 void posix_soc_clean_up(void)
135 {
136 	nce_terminate(nce_st);
137 	posix_arch_clean_up();
138 	run_native_tasks(_NATIVE_ON_EXIT_LEVEL);
139 }
140