1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * Backend for the UART(E) which connects the Tx and Rx of the same UART instance in loopback
9 */
10
11 #include <stdint.h>
12 #include "bs_tracing.h"
13 #include "bs_cmd_line.h"
14 #include "bs_utils.h"
15 #include "bs_dynargs.h"
16 #include "NHW_config.h"
17 #include "NHW_peri_types.h"
18 #include "NHW_UART_backend_if.h"
19 #include "nsi_tasks.h"
20 #include "nsi_hw_scheduler.h"
21 #include "nsi_hws_models_if.h"
22
23 bs_time_t nhw_Timer_ULoopback = TIME_NEVER;
24
25 struct ublb_st_t {
26 bool enabled;
27 bs_time_t Timer;
28
29 char rx_byte;
30 } ublb_st[NHW_UARTE_TOTAL_INST];
31
32 void nhw_uarte_update_common_timer(void);
33
34 static void nhw_ublb_tx_byte(uint inst, uint8_t data);
35 static void nhw_ublb_RTS_pin_toggle(uint inst, bool new_level);
36
nhw_ublb_init(void)37 static void nhw_ublb_init(void) {
38
39 struct backend_if st;
40 st.tx_byte_f = nhw_ublb_tx_byte;
41 st.RTS_pin_toggle_f = nhw_ublb_RTS_pin_toggle;
42 st.uart_enable_notify_f = NULL;
43
44 for (int i = 0; i < NHW_UARTE_TOTAL_INST; i++) {
45
46 ublb_st[i].Timer = TIME_NEVER;
47
48 if (!ublb_st[i].enabled) {
49 continue;
50 }
51
52 nhw_UARTE_backend_register(i, &st);
53 nhw_UARTE_CTS_raised(i);
54 }
55 }
56
57 NSI_TASK(nhw_ublb_init, HW_INIT, 100); /* this must be before the uart itself */
58
nhw_ublb_register_cmdline(void)59 static void nhw_ublb_register_cmdline(void) {
60 static bs_args_struct_t args[NHW_UARTE_TOTAL_INST + 1 /* End marker */];
61 static char descr[] = "Connect this UART instance in loopback (Tx->Rx, RTS->CTS)";
62 #define OPTION_LEN (4 + 2 + 9 + 1)
63 static char options[NHW_UARTE_TOTAL_INST][OPTION_LEN];
64
65 for (int i = 0 ; i < NHW_UARTE_TOTAL_INST; i++) {
66 snprintf(options[i], OPTION_LEN, "uart%i_loopback", i);
67
68 args[i].is_switch = true;
69 args[i].option = options[i];
70 args[i].type = 'b';
71 args[i].dest = &ublb_st[i].enabled;
72 args[i].descript = descr;
73 }
74
75 bs_add_extra_dynargs(args);
76 }
77
78 NSI_TASK(nhw_ublb_register_cmdline, PRE_BOOT_1, 200);
79
nhw_ublb_update_timer(void)80 static void nhw_ublb_update_timer(void) {
81 nhw_Timer_ULoopback = TIME_NEVER;
82 for (int i = 0; i < NHW_UARTE_TOTAL_INST; i++) {
83 if (!ublb_st[i].enabled) {
84 continue;
85 }
86 nhw_Timer_ULoopback = BS_MIN(ublb_st[i].Timer, nhw_Timer_ULoopback);
87 }
88 nhw_uarte_update_common_timer();
89 }
90
nhw_ublb_tx_byte(uint inst,uint8_t data)91 static void nhw_ublb_tx_byte(uint inst, uint8_t data) {
92 if (ublb_st[inst].Timer != TIME_NEVER) {
93 bs_trace_error_time_line("%s: Unexpected error\n", __func__);
94 }
95 ublb_st[inst].rx_byte = data;
96 ublb_st[inst].Timer = nsi_hws_get_time() + nhw_uarte_one_byte_time(inst);
97 nhw_ublb_update_timer();
98 }
99
nhw_ublb_RTS_pin_toggle(uint inst,bool new_level)100 static void nhw_ublb_RTS_pin_toggle(uint inst, bool new_level) {
101 if (new_level){
102 nhw_UARTE_CTS_raised(inst);
103 } else {
104 nhw_UARTE_CTS_lowered(inst);
105 }
106 }
107
nhw_ublb_timer_triggered(void)108 void nhw_ublb_timer_triggered(void) {
109 bs_time_t current_time = nhw_Timer_ULoopback;
110 for (int i = 0; i < NHW_UARTE_TOTAL_INST; i++) {
111 if (ublb_st[i].Timer == current_time) {
112 nhw_UARTE_digest_Rx_byte(i, ublb_st[i].rx_byte);
113 ublb_st[i].Timer = TIME_NEVER;
114 }
115 }
116 nhw_ublb_update_timer();
117 }
118