1 /*
2  * Copyright (c) 2019 omSquare s.r.o.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/uart.h>
8 #include <zephyr/kernel.h>
9 #include <SEGGER_RTT.h>
10 
11 #define DT_DRV_COMPAT segger_rtt_uart
12 
13 extern struct k_mutex rtt_term_mutex;
14 
15 struct uart_rtt_config {
16 	void *up_buffer;
17 	size_t up_size;
18 	void *down_buffer;
19 	size_t down_size;
20 	uint8_t channel;
21 };
22 
23 struct uart_rtt_data {
24 #ifdef CONFIG_UART_ASYNC_API
25 	uart_callback_t callback;
26 	void *user_data;
27 #endif /* CONFIG_UART_ASYNC_API */
28 };
29 
uart_rtt_init(const struct device * dev)30 static int uart_rtt_init(const struct device *dev)
31 {
32 	/*
33 	 * Channel 0 is initialized at compile-time, Kconfig ensures that
34 	 * it is configured in correct, non-blocking mode. Other channels
35 	 * need to be configured at run-time.
36 	 */
37 	if (dev->config) {
38 		const struct uart_rtt_config *cfg = dev->config;
39 
40 		SEGGER_RTT_ConfigUpBuffer(cfg->channel, dev->name,
41 					  cfg->up_buffer, cfg->up_size,
42 					  SEGGER_RTT_MODE_NO_BLOCK_SKIP);
43 		SEGGER_RTT_ConfigDownBuffer(cfg->channel, dev->name,
44 					    cfg->down_buffer, cfg->down_size,
45 					    SEGGER_RTT_MODE_NO_BLOCK_SKIP);
46 	}
47 	return 0;
48 }
49 
50 /**
51  * @brief Poll the device for input.
52  *
53  * @param dev UART device struct
54  * @param c Pointer to character
55  *
56  * @return 0 if a character arrived, -1 if the input buffer if empty.
57  */
58 
uart_rtt_poll_in(const struct device * dev,unsigned char * c)59 static int uart_rtt_poll_in(const struct device *dev, unsigned char *c)
60 {
61 	const struct uart_rtt_config *config = dev->config;
62 	unsigned int ch = config ? config->channel : 0;
63 	unsigned int ret = SEGGER_RTT_Read(ch, c, 1);
64 
65 	return ret ? 0 : -1;
66 }
67 
68 /**
69  * @brief Output a character in polled mode.
70  *
71  * @param dev UART device struct
72  * @param c Character to send
73  */
uart_rtt_poll_out(const struct device * dev,unsigned char c)74 static void uart_rtt_poll_out(const struct device *dev, unsigned char c)
75 {
76 	const struct uart_rtt_config *config = dev->config;
77 	unsigned int ch = config ? config->channel : 0;
78 
79 	SEGGER_RTT_Write(ch, &c, 1);
80 }
81 
82 #ifdef CONFIG_UART_ASYNC_API
83 
uart_rtt_callback_set(const struct device * dev,uart_callback_t callback,void * user_data)84 static int uart_rtt_callback_set(const struct device *dev,
85 				 uart_callback_t callback, void *user_data)
86 {
87 	struct uart_rtt_data *data = dev->data;
88 
89 	data->callback = callback;
90 	data->user_data = user_data;
91 	return 0;
92 }
93 
uart_rtt_tx(const struct device * dev,const uint8_t * buf,size_t len,int32_t timeout)94 static int uart_rtt_tx(const struct device *dev,
95 		       const uint8_t *buf, size_t len, int32_t timeout)
96 {
97 	const struct uart_rtt_config *cfg = dev->config;
98 	struct uart_rtt_data *data = dev->data;
99 	unsigned int ch = cfg ? cfg->channel : 0;
100 
101 	ARG_UNUSED(timeout);
102 
103 	/* RTT mutex cannot be claimed in ISRs */
104 	if (k_is_in_isr()) {
105 		return -ENOTSUP;
106 	}
107 
108 	/* Claim the RTT lock */
109 	if (k_mutex_lock(&rtt_term_mutex, K_NO_WAIT) != 0) {
110 		return -EBUSY;
111 	}
112 
113 	/* Output the buffer */
114 	SEGGER_RTT_WriteNoLock(ch, buf, len);
115 
116 	/* Return RTT lock */
117 	SEGGER_RTT_UNLOCK();
118 
119 	/* Send the TX complete callback */
120 	if (data->callback) {
121 		struct uart_event evt = {
122 			.type = UART_TX_DONE,
123 			.data.tx.buf = buf,
124 			.data.tx.len = len
125 		};
126 		data->callback(dev, &evt, data->user_data);
127 	}
128 
129 	return 0;
130 }
131 
uart_rtt_tx_abort(const struct device * dev)132 static int uart_rtt_tx_abort(const struct device *dev)
133 {
134 	/* RTT TX is a memcpy, there is never a transmission to abort */
135 	ARG_UNUSED(dev);
136 
137 	return -EFAULT;
138 }
139 
uart_rtt_rx_enable(const struct device * dev,uint8_t * buf,size_t len,int32_t timeout)140 static int uart_rtt_rx_enable(const struct device *dev,
141 			      uint8_t *buf, size_t len, int32_t timeout)
142 {
143 	/* SEGGER RTT reception is implemented as a direct memory write to RAM
144 	 * by a connected debugger. As such there is no hardware interrupt
145 	 * or other mechanism to know when the debugger has added data to be
146 	 * read. Asynchronous RX does not make sense in such a context, and is
147 	 * therefore not supported.
148 	 */
149 	ARG_UNUSED(dev);
150 	ARG_UNUSED(buf);
151 	ARG_UNUSED(len);
152 	ARG_UNUSED(timeout);
153 
154 	return -ENOTSUP;
155 }
156 
uart_rtt_rx_disable(const struct device * dev)157 static int uart_rtt_rx_disable(const struct device *dev)
158 {
159 	/* Asynchronous RX not supported, see uart_rtt_rx_enable */
160 	ARG_UNUSED(dev);
161 
162 	return -EFAULT;
163 }
164 
uart_rtt_rx_buf_rsp(const struct device * dev,uint8_t * buf,size_t len)165 static int uart_rtt_rx_buf_rsp(const struct device *dev,
166 			       uint8_t *buf, size_t len)
167 {
168 	/* Asynchronous RX not supported, see uart_rtt_rx_enable */
169 	ARG_UNUSED(dev);
170 	ARG_UNUSED(buf);
171 	ARG_UNUSED(len);
172 
173 	return -ENOTSUP;
174 }
175 
176 #endif /* CONFIG_UART_ASYNC_API */
177 
178 static const struct uart_driver_api uart_rtt_driver_api = {
179 	.poll_in = uart_rtt_poll_in,
180 	.poll_out = uart_rtt_poll_out,
181 #ifdef CONFIG_UART_ASYNC_API
182 	.callback_set = uart_rtt_callback_set,
183 	.tx = uart_rtt_tx,
184 	.tx_abort = uart_rtt_tx_abort,
185 	.rx_enable = uart_rtt_rx_enable,
186 	.rx_buf_rsp = uart_rtt_rx_buf_rsp,
187 	.rx_disable = uart_rtt_rx_disable,
188 #endif /* CONFIG_UART_ASYNC_API */
189 };
190 
191 #define UART_RTT(idx)                   DT_NODELABEL(rtt##idx)
192 #define UART_RTT_PROP(idx, prop)        DT_PROP(UART_RTT(idx), prop)
193 #define UART_RTT_CONFIG_NAME(idx)       uart_rtt##idx##_config
194 
195 #define UART_RTT_CONFIG(idx)						    \
196 	static								    \
197 	uint8_t uart_rtt##idx##_tx_buf[UART_RTT_PROP(idx, tx_buffer_size)]; \
198 	static								    \
199 	uint8_t uart_rtt##idx##_rx_buf[UART_RTT_PROP(idx, rx_buffer_size)]; \
200 									    \
201 	static const struct uart_rtt_config UART_RTT_CONFIG_NAME(idx) = {   \
202 		.up_buffer = uart_rtt##idx##_tx_buf,			    \
203 		.up_size = sizeof(uart_rtt##idx##_tx_buf),		    \
204 		.down_buffer = uart_rtt##idx##_rx_buf,			    \
205 		.down_size = sizeof(uart_rtt##idx##_rx_buf),		    \
206 		.channel = idx,						    \
207 	}
208 
209 #define UART_RTT_INIT(idx, config)					      \
210 	struct uart_rtt_data uart_rtt##idx##_data;			      \
211 									      \
212 	DEVICE_DT_DEFINE(UART_RTT(idx), uart_rtt_init, NULL, \
213 			    &uart_rtt##idx##_data, config,		      \
214 			    PRE_KERNEL_2, CONFIG_SERIAL_INIT_PRIORITY,	      \
215 			    &uart_rtt_driver_api)
216 
217 #ifdef CONFIG_UART_RTT_0
218 UART_RTT_INIT(0, NULL);
219 #endif
220 
221 #ifdef CONFIG_UART_RTT_1
222 UART_RTT_CONFIG(1);
223 UART_RTT_INIT(1, &UART_RTT_CONFIG_NAME(1));
224 #endif
225 
226 #ifdef CONFIG_UART_RTT_2
227 UART_RTT_CONFIG(2);
228 UART_RTT_INIT(2, &UART_RTT_CONFIG_NAME(2));
229 #endif
230 
231 #ifdef CONFIG_UART_RTT_3
232 UART_RTT_CONFIG(3);
233 UART_RTT_INIT(3, &UART_RTT_CONFIG_NAME(3));
234 #endif
235