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