1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * GPIOTE - GPIO tasks and events
9 * https://infocenter.nordicsemi.com/topic/ps_nrf52833/gpiote.html?cp=5_1_0_5_8
10 *
11 * This model has the following limitations:
12 * * INTENCLR will always read as 0
13 * * Unlike in real HW, tasks cannot occur simultaneously, they always happen in some sequence
14 * so task priority is not accounted for
15 */
16
17 #include <stdint.h>
18 #include <string.h>
19 #include "NHW_common_types.h"
20 #include "NHW_config.h"
21 #include "NHW_peri_types.h"
22 #include "NRF_GPIOTE.h"
23 #include "NRF_GPIO.h"
24 #include "NRF_PPI.h"
25 #include "irq_ctrl.h"
26 #include "bs_tracing.h"
27 #include "nsi_tasks.h"
28
29 NRF_GPIOTE_Type NRF_GPIOTE_regs = {0};
30 /* Mapping of peripheral instance to {int controller instance, int number} */
31 static struct nhw_irq_mapping nhw_gpiote_irq_map[NHW_GPIOTE_TOTAL_INST] = NHW_GPIOTE_INT_MAP;
32
33 static uint32_t GPIOTE_ITEN;
34 static bool gpiote_int_line; /* Is the GPIOTE currently driving its interrupt line high */
35
36 /* For each GPIO channel, its status */
37 static struct gpiote_ch_status_t {
38 uint8_t mode;
39 uint8_t port; /* GPIO instance */
40 uint8_t pin; /* GPIO pin in that instance (psel) */
41 uint8_t polarity; /* Content of the CONFIG[].polarity field */
42 /* Level at which the GPIOTE has been driving this pin,
43 * or which it has been getting from the GPIO
44 * true = high, false = low */
45 bool level;
46 } gpiote_ch_status[N_GPIOTE_CHANNELS];
47
48 /**
49 * Initialize the GPIOs model
50 */
nrf_gpiote_init(void)51 static void nrf_gpiote_init(void) {
52 memset(&NRF_GPIOTE_regs, 0, sizeof(NRF_GPIOTE_regs));
53 }
54
55 NSI_TASK(nrf_gpiote_init, HW_INIT, 100);
56
57 static void nrf_gpiote_events_port(void);
58
59 /*
60 * API to the GPIO components, in which they can signal that their DETECT output signal
61 * has been raised.
62 *
63 * We do not keep track in the GPIOTE about the signal being lowered, as the GPIOTE
64 * only reacts to raising edges.
65 * Therefore it is the responsibility of the GPIO models to call this if and only
66 * if there is a raising edge on their DETECT output signal.
67 */
nrf_gpiote_port_event_raise(unsigned int port)68 void nrf_gpiote_port_event_raise(unsigned int port) {
69 (void)port; /* unused */
70 nrf_gpiote_events_port();
71 }
72
nrf_gpiote_TASKS_OUT(unsigned int n)73 void nrf_gpiote_TASKS_OUT(unsigned int n) {
74 struct gpiote_ch_status_t *sc = &gpiote_ch_status[n];
75 if (sc->mode != GPIOTE_CONFIG_MODE_Task) {
76 return;
77 }
78 switch (sc->polarity) {
79 case GPIOTE_CONFIG_POLARITY_None:
80 return;
81 case GPIOTE_CONFIG_POLARITY_LoToHi:
82 sc->level = true;
83 break;
84 case GPIOTE_CONFIG_POLARITY_HiToLo:
85 sc->level = false;
86 break;
87 case GPIOTE_CONFIG_POLARITY_Toggle:
88 sc->level = !sc->level;
89 break;
90 default: /* LCOV_EXCL_START */
91 bs_trace_error_time_line("%s: Missconfigured CONFIG.CONFIG[%i]\n", n);
92 break;
93 } /* LCOV_EXCL_STOP */
94 /* We may be calling the GPIO even if we haven't changed it, but that is fine */
95 nrf_gpio_peri_change_output(sc->port, sc->pin, sc->level);
96 }
97
nrf_gpiote_TASKS_SET(unsigned int n)98 void nrf_gpiote_TASKS_SET(unsigned int n) {
99 struct gpiote_ch_status_t *sc = &gpiote_ch_status[n];
100 if (sc->mode != GPIOTE_CONFIG_MODE_Task) {
101 return;
102 }
103 sc->level = true;
104 /* We may be calling the GPIO even if we haven't changed it, but that is fine */
105 nrf_gpio_peri_change_output(sc->port, sc->pin, sc->level);
106 }
107
nrf_gpiote_TASKS_CLR(unsigned int n)108 void nrf_gpiote_TASKS_CLR(unsigned int n) {
109 struct gpiote_ch_status_t *sc = &gpiote_ch_status[n];
110 if (sc->mode != GPIOTE_CONFIG_MODE_Task) {
111 return;
112 }
113 sc->level = false;
114 /* We may be calling the GPIO even if we haven't changed it, but that is fine */
115 nrf_gpio_peri_change_output(sc->port, sc->pin, sc->level);
116 }
117
nrf_gpiote_eval_interrupt(void)118 static void nrf_gpiote_eval_interrupt(void) {
119 bool new_int_line = false;
120 int mask;
121
122 for (int i = 0; i < N_GPIOTE_CHANNELS; i++) {
123 mask = (GPIOTE_ITEN >> i) & 0x1;
124 if (NRF_GPIOTE_regs.EVENTS_IN[i] && mask) {
125 new_int_line = true;
126 break; /* No need to check more */
127 }
128 }
129 mask = (GPIOTE_ITEN & GPIOTE_INTENCLR_PORT_Msk) >> GPIOTE_INTENCLR_PORT_Pos;
130 if (NRF_GPIOTE_regs.EVENTS_PORT && mask) {
131 new_int_line = true;
132 }
133
134 int inst = 0;
135 if (gpiote_int_line == false && new_int_line == true) {
136 gpiote_int_line = true;
137 hw_irq_ctrl_raise_level_irq_line(nhw_gpiote_irq_map[inst].cntl_inst,
138 nhw_gpiote_irq_map[inst].int_nbr);
139 } else if (gpiote_int_line == true && new_int_line == false) {
140 gpiote_int_line = false;
141 hw_irq_ctrl_lower_level_irq_line(nhw_gpiote_irq_map[inst].cntl_inst,
142 nhw_gpiote_irq_map[inst].int_nbr);
143 }
144 }
145
nrf_gpiote_events_in(unsigned int n)146 static void nrf_gpiote_events_in(unsigned int n) {
147 NRF_GPIOTE_regs.EVENTS_IN[n] = 1;
148 nrf_gpiote_eval_interrupt();
149 nrf_ppi_event(GPIOTE_EVENTS_IN_0 + n);
150 }
151
nrf_gpiote_events_port(void)152 static void nrf_gpiote_events_port(void) {
153 NRF_GPIOTE_regs.EVENTS_PORT = 1;
154 nrf_gpiote_eval_interrupt();
155 nrf_ppi_event(GPIOTE_EVENTS_PORT);
156 }
157
158 /*
159 * Function to be called (by the GPIO model) when a pin changes
160 * for which an EVENTS_IN is registered
161 */
nrf_gpiote_input_change_ntf(unsigned int port,unsigned int n,bool value)162 static void nrf_gpiote_input_change_ntf(unsigned int port, unsigned int n, bool value)
163 {
164 int i;
165 struct gpiote_ch_status_t *sc;
166 bool generate_event = false;
167
168 /* Find event we are connecting this pin to */
169 for (i = 0; i < N_GPIOTE_CHANNELS; i++) {
170 sc = &gpiote_ch_status[i];
171 if (sc->port == port && sc->pin == n) {
172 break;
173 }
174 }
175 if (i == N_GPIOTE_CHANNELS) { /* LCOV_EXCL_START */
176 bs_trace_error_time_line("%s: Programming error, received notification from not "
177 "connected GPIO port.pin:%i.%i\n", port, n);
178 } /* LCOV_EXCL_STOP */
179
180 switch (sc->polarity) {
181 case GPIOTE_CONFIG_POLARITY_None:
182 return;
183 case GPIOTE_CONFIG_POLARITY_LoToHi:
184 if ((sc->level == false) && (value == true)) {
185 generate_event = true;
186 }
187 break;
188 case GPIOTE_CONFIG_POLARITY_HiToLo:
189 if ((sc->level == true) && (value == false)) {
190 generate_event = true;
191 }
192 break;
193 case GPIOTE_CONFIG_POLARITY_Toggle:
194 if (sc->level != value) {
195 generate_event = true;
196 }
197 break;
198 default: /* LCOV_EXCL_START */
199 bs_trace_error_time_line("%s: Missconfigured CONFIG.CONFIG[%i]\n", n);
200 break;
201 } /* LCOV_EXCL_STOP */
202 sc->level = value;
203
204 if (generate_event) {
205 nrf_gpiote_events_in(i);
206 }
207 }
208
209 /*
210 * Register write side-effecting functions
211 */
212
nrf_gpiote_regw_sideeffects_TASKS_OUT(unsigned int n)213 void nrf_gpiote_regw_sideeffects_TASKS_OUT(unsigned int n) {
214 if (NRF_GPIOTE_regs.TASKS_OUT[n]) {
215 NRF_GPIOTE_regs.TASKS_OUT[n] = 0;
216 nrf_gpiote_TASKS_OUT(n);
217 }
218 }
219
nrf_gpiote_regw_sideeffects_TASKS_SET(unsigned int n)220 void nrf_gpiote_regw_sideeffects_TASKS_SET(unsigned int n) {
221 if (NRF_GPIOTE_regs.TASKS_SET[n]) {
222 NRF_GPIOTE_regs.TASKS_SET[n] = 0;
223 nrf_gpiote_TASKS_SET(n);
224 }
225 }
226
nrf_gpiote_regw_sideeffects_TASKS_CLR(unsigned int n)227 void nrf_gpiote_regw_sideeffects_TASKS_CLR(unsigned int n) {
228 if (NRF_GPIOTE_regs.TASKS_CLR[n]) {
229 NRF_GPIOTE_regs.TASKS_CLR[n] = 0;
230 nrf_gpiote_TASKS_CLR(n);
231 }
232 }
233
nrf_gpiote_regw_sideeffects_EVENTS_IN(unsigned int n)234 void nrf_gpiote_regw_sideeffects_EVENTS_IN(unsigned int n) {
235 nrf_gpiote_eval_interrupt();
236 }
237
nrf_gpiote_regw_sideeffects_EVENTS_PORT(void)238 void nrf_gpiote_regw_sideeffects_EVENTS_PORT(void) {
239 nrf_gpiote_eval_interrupt();
240
241 }
242
nrf_gpiote_regw_sideeffects_INTENSET(void)243 void nrf_gpiote_regw_sideeffects_INTENSET(void) {
244 if (NRF_GPIOTE_regs.INTENSET) {
245 GPIOTE_ITEN |= NRF_GPIOTE_regs.INTENSET;
246 NRF_GPIOTE_regs.INTENSET = GPIOTE_ITEN;
247 nrf_gpiote_eval_interrupt();
248 }
249 }
250
nrf_gpiote_regw_sideeffects_INTENCLR(void)251 void nrf_gpiote_regw_sideeffects_INTENCLR(void) {
252 if (NRF_GPIOTE_regs.INTENCLR) {
253 GPIOTE_ITEN &= ~NRF_GPIOTE_regs.INTENCLR;
254 NRF_GPIOTE_regs.INTENCLR = 0;
255 nrf_gpiote_eval_interrupt();
256 }
257 }
258
nrf_gpiote_regw_sideeffects_CONFIG(unsigned int n)259 void nrf_gpiote_regw_sideeffects_CONFIG(unsigned int n) {
260 struct gpiote_ch_status_t *sc = &gpiote_ch_status[n];
261 unsigned int mode = NRF_GPIOTE_regs.CONFIG[n] & GPIOTE_CONFIG_MODE_Msk;
262 unsigned int pin = (NRF_GPIOTE_regs.CONFIG[n] & GPIOTE_CONFIG_PSEL_Msk)
263 >>GPIOTE_CONFIG_PSEL_Pos;
264 unsigned int port = (NRF_GPIOTE_regs.CONFIG[n] & GPIOTE_CONFIG_PORT_Msk)
265 >>GPIOTE_CONFIG_PORT_Pos;
266 unsigned int polarity = (NRF_GPIOTE_regs.CONFIG[n] & GPIOTE_CONFIG_POLARITY_Msk)
267 >>GPIOTE_CONFIG_POLARITY_Pos;
268 unsigned int outinit = (NRF_GPIOTE_regs.CONFIG[n] & GPIOTE_CONFIG_OUTINIT_Msk)
269 >>GPIOTE_CONFIG_OUTINIT_Pos;
270
271 if ((port != sc->port) || (pin != sc->pin)
272 || (mode == GPIOTE_CONFIG_MODE_Disabled && sc->mode != GPIOTE_CONFIG_MODE_Disabled)) {
273 /* Disconnect the old GPIO pin from the GPIOTE */
274 nrf_gpio_peri_pin_control(sc->port, sc->pin, 0, 0, 0, NULL, -1);
275 }
276
277 sc->mode = mode;
278 sc->pin = pin;
279 sc->port = port;
280 sc->polarity = polarity;
281
282 if (mode == GPIOTE_CONFIG_MODE_Event) {
283 sc->level = nrf_gpio_get_pin_level(port, pin);
284 nrf_gpio_peri_pin_control(port, pin, 1, 3, 2, nrf_gpiote_input_change_ntf, -1);
285 } else if (mode == GPIOTE_CONFIG_MODE_Task) {
286 sc->level = outinit;
287 nrf_gpio_peri_pin_control(port, pin, 1, 2, 3, NULL, outinit);
288 } else if (mode != GPIOTE_CONFIG_MODE_Disabled) { /* LCOV_EXCL_START */
289 bs_trace_error_time_line("%s: GPIOTE.CONFIG[%u].mode configured to an illegal "
290 "value(%u)\n", __func__, n, mode);
291 } /* LCOV_EXCL_STOP */
292 }
293
294 /*
295 * Trampolines to automatically call from the PPI
296 */
297 /* Generated with:
298 #! /usr/bin/env python3
299 #GPIOTE.c
300 for task in {"OUT","SET","CLR"}:
301 for i in range(0,8):
302 print("void nrf_gpiote_TASKS_%s_%i(void){ nrf_gpiote_TASKS_%s(%i); }"%(task,i,task,i))
303 #GPIOTE.h
304 for task in {"OUT","SET","CLR"}:
305 for i in range(0,8):
306 print("void nrf_gpiote_TASKS_%s_%i(void);"%(task,i))
307 */
nrf_gpiote_TASKS_SET_0(void)308 void nrf_gpiote_TASKS_SET_0(void){ nrf_gpiote_TASKS_SET(0); }
nrf_gpiote_TASKS_SET_1(void)309 void nrf_gpiote_TASKS_SET_1(void){ nrf_gpiote_TASKS_SET(1); }
nrf_gpiote_TASKS_SET_2(void)310 void nrf_gpiote_TASKS_SET_2(void){ nrf_gpiote_TASKS_SET(2); }
nrf_gpiote_TASKS_SET_3(void)311 void nrf_gpiote_TASKS_SET_3(void){ nrf_gpiote_TASKS_SET(3); }
nrf_gpiote_TASKS_SET_4(void)312 void nrf_gpiote_TASKS_SET_4(void){ nrf_gpiote_TASKS_SET(4); }
nrf_gpiote_TASKS_SET_5(void)313 void nrf_gpiote_TASKS_SET_5(void){ nrf_gpiote_TASKS_SET(5); }
nrf_gpiote_TASKS_SET_6(void)314 void nrf_gpiote_TASKS_SET_6(void){ nrf_gpiote_TASKS_SET(6); }
nrf_gpiote_TASKS_SET_7(void)315 void nrf_gpiote_TASKS_SET_7(void){ nrf_gpiote_TASKS_SET(7); }
nrf_gpiote_TASKS_CLR_0(void)316 void nrf_gpiote_TASKS_CLR_0(void){ nrf_gpiote_TASKS_CLR(0); }
nrf_gpiote_TASKS_CLR_1(void)317 void nrf_gpiote_TASKS_CLR_1(void){ nrf_gpiote_TASKS_CLR(1); }
nrf_gpiote_TASKS_CLR_2(void)318 void nrf_gpiote_TASKS_CLR_2(void){ nrf_gpiote_TASKS_CLR(2); }
nrf_gpiote_TASKS_CLR_3(void)319 void nrf_gpiote_TASKS_CLR_3(void){ nrf_gpiote_TASKS_CLR(3); }
nrf_gpiote_TASKS_CLR_4(void)320 void nrf_gpiote_TASKS_CLR_4(void){ nrf_gpiote_TASKS_CLR(4); }
nrf_gpiote_TASKS_CLR_5(void)321 void nrf_gpiote_TASKS_CLR_5(void){ nrf_gpiote_TASKS_CLR(5); }
nrf_gpiote_TASKS_CLR_6(void)322 void nrf_gpiote_TASKS_CLR_6(void){ nrf_gpiote_TASKS_CLR(6); }
nrf_gpiote_TASKS_CLR_7(void)323 void nrf_gpiote_TASKS_CLR_7(void){ nrf_gpiote_TASKS_CLR(7); }
nrf_gpiote_TASKS_OUT_0(void)324 void nrf_gpiote_TASKS_OUT_0(void){ nrf_gpiote_TASKS_OUT(0); }
nrf_gpiote_TASKS_OUT_1(void)325 void nrf_gpiote_TASKS_OUT_1(void){ nrf_gpiote_TASKS_OUT(1); }
nrf_gpiote_TASKS_OUT_2(void)326 void nrf_gpiote_TASKS_OUT_2(void){ nrf_gpiote_TASKS_OUT(2); }
nrf_gpiote_TASKS_OUT_3(void)327 void nrf_gpiote_TASKS_OUT_3(void){ nrf_gpiote_TASKS_OUT(3); }
nrf_gpiote_TASKS_OUT_4(void)328 void nrf_gpiote_TASKS_OUT_4(void){ nrf_gpiote_TASKS_OUT(4); }
nrf_gpiote_TASKS_OUT_5(void)329 void nrf_gpiote_TASKS_OUT_5(void){ nrf_gpiote_TASKS_OUT(5); }
nrf_gpiote_TASKS_OUT_6(void)330 void nrf_gpiote_TASKS_OUT_6(void){ nrf_gpiote_TASKS_OUT(6); }
nrf_gpiote_TASKS_OUT_7(void)331 void nrf_gpiote_TASKS_OUT_7(void){ nrf_gpiote_TASKS_OUT(7); }
332