Software APIs
dif_uart.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
6
7#include <assert.h>
8#include <stddef.h>
9
10#include "dif_base.h"
14
15#include "hw/top/uart_regs.h" // Generated.
16
17#define UART_INTR_STATE_MASK 0xffffffffu
18
19const uint32_t kDifUartFifoSizeBytes = 32u;
20
21static bool uart_tx_full(const dif_uart_t *uart) {
22 uint32_t reg = mmio_region_read32(uart->base_addr, UART_STATUS_REG_OFFSET);
23 return bitfield_bit32_read(reg, UART_STATUS_TXFULL_BIT);
24}
25
26static bool uart_tx_idle(const dif_uart_t *uart) {
27 uint32_t reg = mmio_region_read32(uart->base_addr, UART_STATUS_REG_OFFSET);
28 return bitfield_bit32_read(reg, UART_STATUS_TXIDLE_BIT);
29}
30
31static bool uart_rx_empty(const dif_uart_t *uart) {
32 uint32_t reg = mmio_region_read32(uart->base_addr, UART_STATUS_REG_OFFSET);
33 return bitfield_bit32_read(reg, UART_STATUS_RXEMPTY_BIT);
34}
35
36static uint8_t uart_rx_fifo_read(const dif_uart_t *uart) {
37 uint32_t reg = mmio_region_read32(uart->base_addr, UART_RDATA_REG_OFFSET);
38
39 return (uint8_t)bitfield_field32_read(reg, UART_RDATA_RDATA_FIELD);
40}
41
42static void uart_tx_fifo_write(const dif_uart_t *uart, uint8_t byte) {
43 uint32_t reg = bitfield_field32_write(0, UART_WDATA_WDATA_FIELD, byte);
44 mmio_region_write32(uart->base_addr, UART_WDATA_REG_OFFSET, reg);
45}
46
47static void uart_reset(const dif_uart_t *uart) {
48 mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, 0u);
49
50 // Write to the relevant bits clears the FIFOs.
51 uint32_t reg = 0;
52 reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_RXRST_BIT, true);
53 reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_TXRST_BIT, true);
54 mmio_region_write32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET, reg);
55
56 mmio_region_write32(uart->base_addr, UART_OVRD_REG_OFFSET, 0u);
57 mmio_region_write32(uart->base_addr, UART_TIMEOUT_CTRL_REG_OFFSET, 0u);
58 mmio_region_write32(uart->base_addr, UART_INTR_ENABLE_REG_OFFSET, 0u);
59 mmio_region_write32(uart->base_addr, UART_INTR_STATE_REG_OFFSET,
60 UART_INTR_STATE_MASK);
61}
62
63/**
64 * Write up to `bytes_requested` number of bytes to the TX FIFO.
65 */
66static size_t uart_bytes_send(const dif_uart_t *uart, const uint8_t *data,
67 size_t bytes_requested) {
68 size_t bytes_written = 0;
69 while ((bytes_written < bytes_requested) && !uart_tx_full(uart)) {
70 uart_tx_fifo_write(uart, data[bytes_written]);
71 ++bytes_written;
72 }
73
74 return bytes_written;
75}
76
77/**
78 * Read up to `bytes_requested` number of bytes from the RX FIFO.
79 */
80static size_t uart_bytes_receive(const dif_uart_t *uart, size_t bytes_requested,
81 uint8_t *data) {
82 size_t bytes_read = 0;
83 while ((bytes_read < bytes_requested) && !uart_rx_empty(uart)) {
84 data[bytes_read] = uart_rx_fifo_read(uart);
85 ++bytes_read;
86 }
87
88 return bytes_read;
89}
90
91dif_result_t dif_uart_configure(const dif_uart_t *uart,
92 dif_uart_config_t config) {
93 if (uart == NULL || config.baudrate == 0 || config.clk_freq_hz == 0 ||
94 !dif_is_valid_toggle(config.tx_enable) ||
95 !dif_is_valid_toggle(config.rx_enable)) {
96 return kDifBadArg;
97 }
98
99 // Calculation formula: NCO = 16 * 2^nco_width * baud / fclk.
100
101 // Compute NCO register bit width
102 uint32_t nco_width = 0;
103
104 for (int i = 0; i < 32; i++) {
105 nco_width += (UART_CTRL_NCO_MASK >> i) & 1;
106 }
107
108 static_assert((UART_CTRL_NCO_MASK >> 28) == 0,
109 "NCO bit width exceeds 28 bits.");
110
111 // NCO creates 16x of baudrate. So, in addition to the nco_width,
112 // 2^4 should be multiplied.
113 // If uart baud rate is 1.5Mbps and IO is 24Mhz, NCO is 0x10000, which is over
114 // the NCO width, use NCO = 0xffff for this case since the error is tolerable.
115 // Refer to #4263
116 uint64_t nco =
117 ((uint64_t)config.baudrate == 1500000 && config.clk_freq_hz == 24000000)
118 ? 0xffff
119 : udiv64_slow((uint64_t)config.baudrate << (nco_width + 4),
120 config.clk_freq_hz, NULL);
121 uint32_t nco_masked = nco & UART_CTRL_NCO_MASK;
122
123 // Requested baudrate is too high for the given clock frequency.
124 if (nco != nco_masked) {
125 return kDifBadArg;
126 }
127
128 // Check requested RXBLVL is within bounds.
129 uint32_t rxblvl = config.rx_break_level;
130 if ((rxblvl & UART_CTRL_RXBLVL_MASK) != rxblvl) {
131 return kDifBadArg;
132 }
133
134 // Must be called before the first write to any of the UART registers.
135 uart_reset(uart);
136
137 // Set baudrate, enable RX and TX, configure parity.
138 uint32_t reg = 0;
139 reg = bitfield_field32_write(reg, UART_CTRL_NCO_FIELD, nco_masked);
140 reg = bitfield_field32_write(reg, UART_CTRL_RXBLVL_FIELD, rxblvl);
141 if (dif_toggle_to_bool(config.tx_enable)) {
142 reg = bitfield_bit32_write(reg, UART_CTRL_TX_BIT, true);
143 }
144 if (dif_toggle_to_bool(config.rx_enable)) {
145 reg = bitfield_bit32_write(reg, UART_CTRL_RX_BIT, true);
146 }
147 if (config.parity_enable == kDifToggleEnabled) {
148 reg = bitfield_bit32_write(reg, UART_CTRL_PARITY_EN_BIT, true);
149 }
150 if (config.parity == kDifUartParityOdd) {
151 reg = bitfield_bit32_write(reg, UART_CTRL_PARITY_ODD_BIT, true);
152 }
153 mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, reg);
154
155 return kDifOk;
156}
157
158dif_result_t dif_uart_rx_break_level_set(
159 const dif_uart_t *uart, dif_uart_rx_break_level_t rx_break_level) {
160 if (uart == NULL) {
161 return kDifBadArg;
162 }
163
164 uint32_t reg = mmio_region_read32(uart->base_addr, UART_CTRL_REG_OFFSET);
165 reg = bitfield_field32_write(reg, UART_CTRL_RXBLVL_FIELD, rx_break_level);
166 mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, reg);
167
168 return kDifOk;
169}
170
171dif_result_t dif_uart_watermark_rx_set(const dif_uart_t *uart,
172 dif_uart_watermark_t watermark) {
173 if (uart == NULL) {
174 return kDifBadArg;
175 }
176
177 // Check if the requested watermark is valid, and get a corresponding
178 // register definition to be written.
179 uint32_t value;
180 switch (watermark) {
182 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL1;
183 break;
185 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL2;
186 break;
188 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL4;
189 break;
191 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL8;
192 break;
194 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL16;
195 break;
197 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL32;
198 break;
200 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL62;
201 break;
202 default:
203 return kDifError;
204 }
205
206 // Set watermark level.
207 uint32_t reg = mmio_region_read32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET);
208 reg = bitfield_field32_write(reg, UART_FIFO_CTRL_RXILVL_FIELD, value);
209 mmio_region_write32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET, reg);
210
211 return kDifOk;
212}
213
214dif_result_t dif_uart_watermark_tx_set(const dif_uart_t *uart,
215 dif_uart_watermark_t watermark) {
216 if (uart == NULL) {
217 return kDifBadArg;
218 }
219
220 // Check if the requested watermark is valid, and get a corresponding
221 // register definition to be written.
222 uint32_t value;
223 switch (watermark) {
225 value = UART_FIFO_CTRL_TXILVL_VALUE_TXLVL1;
226 break;
228 value = UART_FIFO_CTRL_TXILVL_VALUE_TXLVL2;
229 break;
231 value = UART_FIFO_CTRL_TXILVL_VALUE_TXLVL4;
232 break;
234 value = UART_FIFO_CTRL_TXILVL_VALUE_TXLVL8;
235 break;
237 value = UART_FIFO_CTRL_TXILVL_VALUE_TXLVL16;
238 break;
239 default:
240 // The minimal TX watermark is 1 byte, maximal 16 bytes.
241 return kDifError;
242 }
243
244 // Set watermark level.
245 uint32_t reg = mmio_region_read32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET);
246 reg = bitfield_field32_write(reg, UART_FIFO_CTRL_TXILVL_FIELD, value);
247 mmio_region_write32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET, reg);
248
249 return kDifOk;
250}
251
252dif_result_t dif_uart_set_enable(const dif_uart_t *uart,
253 dif_uart_datapath_t datapath,
254 dif_toggle_t enabled) {
255 if (uart == NULL || !dif_is_valid_toggle(enabled)) {
256 return kDifBadArg;
257 }
258
259 uint32_t reg = mmio_region_read32(uart->base_addr, UART_CTRL_REG_OFFSET);
260
261 switch (datapath) {
263 reg = bitfield_bit32_write(reg, UART_CTRL_RX_BIT,
264 dif_toggle_to_bool(enabled));
265 break;
267 reg = bitfield_bit32_write(reg, UART_CTRL_TX_BIT,
268 dif_toggle_to_bool(enabled));
269 break;
271 reg = bitfield_bit32_write(reg, UART_CTRL_RX_BIT,
272 dif_toggle_to_bool(enabled));
273 reg = bitfield_bit32_write(reg, UART_CTRL_TX_BIT,
274 dif_toggle_to_bool(enabled));
275 break;
276 default:
277 return kDifBadArg;
278 }
279
280 mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, reg);
281
282 return kDifOk;
283}
284
285dif_result_t dif_uart_bytes_send(const dif_uart_t *uart, const uint8_t *data,
286 size_t bytes_requested,
287 size_t *bytes_written) {
288 if (uart == NULL || data == NULL) {
289 return kDifBadArg;
290 }
291
292 // `bytes_written` is an optional parameter.
293 size_t res = uart_bytes_send(uart, data, bytes_requested);
294 if (bytes_written != NULL) {
295 *bytes_written = res;
296 }
297
298 return kDifOk;
299}
300
301dif_result_t dif_uart_bytes_receive(const dif_uart_t *uart,
302 size_t bytes_requested, uint8_t *data,
303 size_t *bytes_read) {
304 if (uart == NULL || data == NULL) {
305 return kDifBadArg;
306 }
307
308 // `bytes_read` is an optional parameter.
309 size_t res = uart_bytes_receive(uart, bytes_requested, data);
310 if (bytes_read != NULL) {
311 *bytes_read = res;
312 }
313
314 return kDifOk;
315}
316
317dif_result_t dif_uart_byte_send_polled(const dif_uart_t *uart, uint8_t byte) {
318 if (uart == NULL) {
319 return kDifBadArg;
320 }
321
322 // Busy wait for the TX FIFO to free up.
323 while (uart_tx_full(uart)) {
324 }
325
326 (void)uart_bytes_send(uart, &byte, 1);
327
328 // Busy wait for the TX FIFO to be drained and for HW to finish processing
329 // the last byte.
330 while (!uart_tx_idle(uart)) {
331 }
332
333 return kDifOk;
334}
335
336dif_result_t dif_uart_byte_receive_polled(const dif_uart_t *uart,
337 uint8_t *byte) {
338 if (uart == NULL || byte == NULL) {
339 return kDifBadArg;
340 }
341
342 // Busy wait for the RX message in the FIFO.
343 while (uart_rx_empty(uart)) {
344 }
345
346 (void)uart_bytes_receive(uart, 1, byte);
347
348 return kDifOk;
349}
350
351dif_result_t dif_uart_rx_bytes_available(const dif_uart_t *uart,
352 size_t *num_bytes) {
353 if (uart == NULL || num_bytes == NULL) {
354 return kDifBadArg;
355 }
356
357 // RX FIFO fill level (in bytes).
358 uint32_t reg =
359 mmio_region_read32(uart->base_addr, UART_FIFO_STATUS_REG_OFFSET);
360 *num_bytes = (size_t)bitfield_field32_read(reg, UART_FIFO_STATUS_RXLVL_FIELD);
361
362 return kDifOk;
363}
364
365dif_result_t dif_uart_tx_bytes_available(const dif_uart_t *uart,
366 size_t *num_bytes) {
367 if (uart == NULL || num_bytes == NULL) {
368 return kDifBadArg;
369 }
370
371 // TX FIFO fill level (in bytes).
372 uint32_t reg =
373 mmio_region_read32(uart->base_addr, UART_FIFO_STATUS_REG_OFFSET);
374 uint32_t fill_bytes =
375 bitfield_field32_read(reg, UART_FIFO_STATUS_TXLVL_FIELD);
376 *num_bytes = kDifUartFifoSizeBytes - fill_bytes;
377
378 return kDifOk;
379}
380
381dif_result_t dif_uart_fifo_reset(const dif_uart_t *uart,
382 dif_uart_datapath_t fifo) {
383 if (uart == NULL) {
384 return kDifBadArg;
385 }
386
387 uint32_t reg = mmio_region_read32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET);
388
389 switch (fifo) {
391 reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_RXRST_BIT, true);
392 break;
394 reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_TXRST_BIT, true);
395 break;
397 reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_RXRST_BIT, true);
398 reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_TXRST_BIT, true);
399 break;
400 default:
401 return kDifBadArg;
402 }
403
404 mmio_region_write32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET, reg);
405
406 return kDifOk;
407}
408
409dif_result_t dif_uart_loopback_set(const dif_uart_t *uart,
410 dif_uart_loopback_t loopback,
411 dif_toggle_t enable) {
412 if (uart == NULL) {
413 return kDifBadArg;
414 }
415
416 uint32_t index = loopback ? UART_CTRL_LLPBK_BIT : UART_CTRL_SLPBK_BIT;
417 uint32_t reg = mmio_region_read32(uart->base_addr, UART_CTRL_REG_OFFSET);
418 reg = bitfield_bit32_write(reg, index, enable == kDifToggleEnabled);
419 mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, reg);
420
421 return kDifOk;
422}
423
424dif_result_t dif_uart_enable_rx_timeout(const dif_uart_t *uart,
425 uint32_t duration_ticks) {
426 if (uart == NULL ||
427 (duration_ticks & ~(uint32_t)UART_TIMEOUT_CTRL_VAL_MASK) != 0) {
428 return kDifBadArg;
429 }
430
431 uint32_t reg = bitfield_bit32_write(0, UART_TIMEOUT_CTRL_EN_BIT, true);
432 reg =
433 bitfield_field32_write(reg, UART_TIMEOUT_CTRL_VAL_FIELD, duration_ticks);
434 mmio_region_write32(uart->base_addr, UART_TIMEOUT_CTRL_REG_OFFSET, reg);
435
436 return kDifOk;
437}
438
439dif_result_t dif_uart_disable_rx_timeout(const dif_uart_t *uart) {
440 if (uart == NULL) {
441 return kDifBadArg;
442 }
443
444 uint32_t reg = bitfield_bit32_write(0, UART_TIMEOUT_CTRL_EN_BIT, false);
445 reg = bitfield_field32_write(reg, UART_TIMEOUT_CTRL_VAL_FIELD, 0);
446 mmio_region_write32(uart->base_addr, UART_TIMEOUT_CTRL_REG_OFFSET, reg);
447
448 return kDifOk;
449}
450
451dif_result_t dif_uart_get_rx_timeout(const dif_uart_t *uart,
453 uint32_t *duration_ticks) {
454 if (uart == NULL || status == NULL) {
455 return kDifBadArg;
456 }
457
458 uint32_t reg =
459 mmio_region_read32(uart->base_addr, UART_TIMEOUT_CTRL_REG_OFFSET);
460 *status = bitfield_bit32_read(reg, UART_TIMEOUT_CTRL_EN_BIT)
463
464 if (duration_ticks != NULL) {
465 *duration_ticks = bitfield_field32_read(reg, UART_TIMEOUT_CTRL_VAL_FIELD);
466 }
467
468 return kDifOk;
469}