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 "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 // Disable interrupts.
156 mmio_region_write32(uart->base_addr, UART_INTR_ENABLE_REG_OFFSET, 0u);
157
158 return kDifOk;
159}
160
161dif_result_t dif_uart_rx_break_level_set(
162 const dif_uart_t *uart, dif_uart_rx_break_level_t rx_break_level) {
163 if (uart == NULL) {
164 return kDifBadArg;
165 }
166
167 uint32_t reg = mmio_region_read32(uart->base_addr, UART_CTRL_REG_OFFSET);
168 reg = bitfield_field32_write(reg, UART_CTRL_RXBLVL_FIELD, rx_break_level);
169 mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, reg);
170
171 return kDifOk;
172}
173
174dif_result_t dif_uart_watermark_rx_set(const dif_uart_t *uart,
175 dif_uart_watermark_t watermark) {
176 if (uart == NULL) {
177 return kDifBadArg;
178 }
179
180 // Check if the requested watermark is valid, and get a corresponding
181 // register definition to be written.
182 uint32_t value;
183 switch (watermark) {
185 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL1;
186 break;
188 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL2;
189 break;
191 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL4;
192 break;
194 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL8;
195 break;
197 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL16;
198 break;
200 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL32;
201 break;
203 value = UART_FIFO_CTRL_RXILVL_VALUE_RXLVL62;
204 break;
205 default:
206 return kDifError;
207 }
208
209 // Set watermark level.
210 uint32_t reg = mmio_region_read32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET);
211 reg = bitfield_field32_write(reg, UART_FIFO_CTRL_RXILVL_FIELD, value);
212 mmio_region_write32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET, reg);
213
214 return kDifOk;
215}
216
217dif_result_t dif_uart_watermark_tx_set(const dif_uart_t *uart,
218 dif_uart_watermark_t watermark) {
219 if (uart == NULL) {
220 return kDifBadArg;
221 }
222
223 // Check if the requested watermark is valid, and get a corresponding
224 // register definition to be written.
225 uint32_t value;
226 switch (watermark) {
228 value = UART_FIFO_CTRL_TXILVL_VALUE_TXLVL1;
229 break;
231 value = UART_FIFO_CTRL_TXILVL_VALUE_TXLVL2;
232 break;
234 value = UART_FIFO_CTRL_TXILVL_VALUE_TXLVL4;
235 break;
237 value = UART_FIFO_CTRL_TXILVL_VALUE_TXLVL8;
238 break;
240 value = UART_FIFO_CTRL_TXILVL_VALUE_TXLVL16;
241 break;
242 default:
243 // The minimal TX watermark is 1 byte, maximal 16 bytes.
244 return kDifError;
245 }
246
247 // Set watermark level.
248 uint32_t reg = mmio_region_read32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET);
249 reg = bitfield_field32_write(reg, UART_FIFO_CTRL_TXILVL_FIELD, value);
250 mmio_region_write32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET, reg);
251
252 return kDifOk;
253}
254
255dif_result_t dif_uart_set_enable(const dif_uart_t *uart,
256 dif_uart_datapath_t datapath,
257 dif_toggle_t enabled) {
258 if (uart == NULL || !dif_is_valid_toggle(enabled)) {
259 return kDifBadArg;
260 }
261
262 uint32_t reg = mmio_region_read32(uart->base_addr, UART_CTRL_REG_OFFSET);
263
264 switch (datapath) {
266 reg = bitfield_bit32_write(reg, UART_CTRL_RX_BIT,
267 dif_toggle_to_bool(enabled));
268 break;
270 reg = bitfield_bit32_write(reg, UART_CTRL_TX_BIT,
271 dif_toggle_to_bool(enabled));
272 break;
274 reg = bitfield_bit32_write(reg, UART_CTRL_RX_BIT,
275 dif_toggle_to_bool(enabled));
276 reg = bitfield_bit32_write(reg, UART_CTRL_TX_BIT,
277 dif_toggle_to_bool(enabled));
278 break;
279 default:
280 return kDifBadArg;
281 }
282
283 mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, reg);
284
285 return kDifOk;
286}
287
288dif_result_t dif_uart_bytes_send(const dif_uart_t *uart, const uint8_t *data,
289 size_t bytes_requested,
290 size_t *bytes_written) {
291 if (uart == NULL || data == NULL) {
292 return kDifBadArg;
293 }
294
295 // `bytes_written` is an optional parameter.
296 size_t res = uart_bytes_send(uart, data, bytes_requested);
297 if (bytes_written != NULL) {
298 *bytes_written = res;
299 }
300
301 return kDifOk;
302}
303
304dif_result_t dif_uart_bytes_receive(const dif_uart_t *uart,
305 size_t bytes_requested, uint8_t *data,
306 size_t *bytes_read) {
307 if (uart == NULL || data == NULL) {
308 return kDifBadArg;
309 }
310
311 // `bytes_read` is an optional parameter.
312 size_t res = uart_bytes_receive(uart, bytes_requested, data);
313 if (bytes_read != NULL) {
314 *bytes_read = res;
315 }
316
317 return kDifOk;
318}
319
320dif_result_t dif_uart_byte_send_polled(const dif_uart_t *uart, uint8_t byte) {
321 if (uart == NULL) {
322 return kDifBadArg;
323 }
324
325 // Busy wait for the TX FIFO to free up.
326 while (uart_tx_full(uart)) {
327 }
328
329 (void)uart_bytes_send(uart, &byte, 1);
330
331 // Busy wait for the TX FIFO to be drained and for HW to finish processing
332 // the last byte.
333 while (!uart_tx_idle(uart)) {
334 }
335
336 return kDifOk;
337}
338
339dif_result_t dif_uart_byte_receive_polled(const dif_uart_t *uart,
340 uint8_t *byte) {
341 if (uart == NULL || byte == NULL) {
342 return kDifBadArg;
343 }
344
345 // Busy wait for the RX message in the FIFO.
346 while (uart_rx_empty(uart)) {
347 }
348
349 (void)uart_bytes_receive(uart, 1, byte);
350
351 return kDifOk;
352}
353
354dif_result_t dif_uart_rx_bytes_available(const dif_uart_t *uart,
355 size_t *num_bytes) {
356 if (uart == NULL || num_bytes == NULL) {
357 return kDifBadArg;
358 }
359
360 // RX FIFO fill level (in bytes).
361 uint32_t reg =
362 mmio_region_read32(uart->base_addr, UART_FIFO_STATUS_REG_OFFSET);
363 *num_bytes = (size_t)bitfield_field32_read(reg, UART_FIFO_STATUS_RXLVL_FIELD);
364
365 return kDifOk;
366}
367
368dif_result_t dif_uart_tx_bytes_available(const dif_uart_t *uart,
369 size_t *num_bytes) {
370 if (uart == NULL || num_bytes == NULL) {
371 return kDifBadArg;
372 }
373
374 // TX FIFO fill level (in bytes).
375 uint32_t reg =
376 mmio_region_read32(uart->base_addr, UART_FIFO_STATUS_REG_OFFSET);
377 uint32_t fill_bytes =
378 bitfield_field32_read(reg, UART_FIFO_STATUS_TXLVL_FIELD);
379 *num_bytes = kDifUartFifoSizeBytes - fill_bytes;
380
381 return kDifOk;
382}
383
384dif_result_t dif_uart_fifo_reset(const dif_uart_t *uart,
385 dif_uart_datapath_t fifo) {
386 if (uart == NULL) {
387 return kDifBadArg;
388 }
389
390 uint32_t reg = mmio_region_read32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET);
391
392 switch (fifo) {
394 reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_RXRST_BIT, true);
395 break;
397 reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_TXRST_BIT, true);
398 break;
400 reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_RXRST_BIT, true);
401 reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_TXRST_BIT, true);
402 break;
403 default:
404 return kDifBadArg;
405 }
406
407 mmio_region_write32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET, reg);
408
409 return kDifOk;
410}
411
412dif_result_t dif_uart_loopback_set(const dif_uart_t *uart,
413 dif_uart_loopback_t loopback,
414 dif_toggle_t enable) {
415 if (uart == NULL) {
416 return kDifBadArg;
417 }
418
419 uint32_t index = loopback ? UART_CTRL_LLPBK_BIT : UART_CTRL_SLPBK_BIT;
420 uint32_t reg = mmio_region_read32(uart->base_addr, UART_CTRL_REG_OFFSET);
421 reg = bitfield_bit32_write(reg, index, enable == kDifToggleEnabled);
422 mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, reg);
423
424 return kDifOk;
425}
426
427dif_result_t dif_uart_enable_rx_timeout(const dif_uart_t *uart,
428 uint32_t duration_ticks) {
429 if (uart == NULL ||
430 (duration_ticks & ~(uint32_t)UART_TIMEOUT_CTRL_VAL_MASK) != 0) {
431 return kDifBadArg;
432 }
433
434 uint32_t reg = bitfield_bit32_write(0, UART_TIMEOUT_CTRL_EN_BIT, true);
435 reg =
436 bitfield_field32_write(reg, UART_TIMEOUT_CTRL_VAL_FIELD, duration_ticks);
437 mmio_region_write32(uart->base_addr, UART_TIMEOUT_CTRL_REG_OFFSET, reg);
438
439 return kDifOk;
440}
441
442dif_result_t dif_uart_disable_rx_timeout(const dif_uart_t *uart) {
443 if (uart == NULL) {
444 return kDifBadArg;
445 }
446
447 uint32_t reg = bitfield_bit32_write(0, UART_TIMEOUT_CTRL_EN_BIT, false);
448 reg = bitfield_field32_write(reg, UART_TIMEOUT_CTRL_VAL_FIELD, 0);
449 mmio_region_write32(uart->base_addr, UART_TIMEOUT_CTRL_REG_OFFSET, reg);
450
451 return kDifOk;
452}
453
454dif_result_t dif_uart_get_rx_timeout(const dif_uart_t *uart,
456 uint32_t *duration_ticks) {
457 if (uart == NULL || status == NULL) {
458 return kDifBadArg;
459 }
460
461 uint32_t reg =
462 mmio_region_read32(uart->base_addr, UART_TIMEOUT_CTRL_REG_OFFSET);
463 *status = bitfield_bit32_read(reg, UART_TIMEOUT_CTRL_EN_BIT)
466
467 if (duration_ticks != NULL) {
468 *duration_ticks = bitfield_field32_read(reg, UART_TIMEOUT_CTRL_VAL_FIELD);
469 }
470
471 return kDifOk;
472}