Software APIs
uart_testutils.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
5#include "sw/device/lib/testing/uart_testutils.h"
6
7#include <assert.h>
8#include <stdbool.h>
9#include <stdint.h>
10
11#include "hw/top/dt/dt_pinmux.h"
18#include "sw/device/lib/testing/pinmux_testutils.h"
19#include "sw/device/lib/testing/test_framework/check.h"
20
21#include "hw/top/uart_regs.h" // Generated.
22
23#define MODULE_ID MAKE_MODULE_ID('u', 't', 'u')
24
25/**
26 * Get the UART instance from index.
27 *
28 * @param uart_idx UART index (0-based).
29 * @return UART DT instance, or kDtUartCount if invalid.
30 */
31static dt_uart_t get_uart_instance(uint8_t uart_idx) {
32 if (uart_idx >= kDtUartCount) {
33 return kDtUartCount;
34 }
35 return (dt_uart_t)uart_idx;
36}
37
38/**
39 * Get the appropriate pads for a UART instance and channel based on the
40 * platform. This replicates the original behavior with different mappings for
41 * DV vs synthesized platforms.
42 *
43 * @param uart_dt UART DT instance.
44 * @param channel The channel to connect the UART to.
45 * @param rx_pad Output parameter for RX pad.
46 * @param tx_pad Output parameter for TX pad.
47 * @return OK_STATUS if successful, error status otherwise.
48 */
49static status_t get_uart_pads_for_channel(dt_uart_t uart_dt,
50 uart_pinmux_channel_t channel,
51 dt_pad_t *rx_pad, dt_pad_t *tx_pad) {
52#if defined(OPENTITAN_IS_DARJEELING)
53 // Darjeeling only has UART0 and uses dedicated pads
54 if (uart_dt != kDtUart0) {
55 return INVALID_ARGUMENT();
56 }
57 *rx_pad = kDtPadUart0Rx;
58 *tx_pad = kDtPadUart0Tx;
59#elif defined(OPENTITAN_IS_EARLGREY) || defined(OPENTITAN_IS_ENGLISHBREAKFAST)
60 // For Earlgrey and EnglishBreakfast platforms
61 // For DV platform, each UART has its own specific mapping
63 switch (uart_dt) {
64 case kDtUart0:
65 *rx_pad = kDtPadIoc3;
66 *tx_pad = kDtPadIoc4;
67 break;
68 case kDtUart1:
69 *rx_pad = kDtPadIob4;
70 *tx_pad = kDtPadIob5;
71 break;
72 case kDtUart2:
73 *rx_pad = kDtPadIoa4;
74 *tx_pad = kDtPadIoa5;
75 break;
76 case kDtUart3:
77 *rx_pad = kDtPadIoa0;
78 *tx_pad = kDtPadIoa1;
79 break;
80 default:
81 return INVALID_ARGUMENT();
82 }
83 } else {
84 // For synthesized platforms, use channel-based mapping
85 switch (channel) {
86 case kUartPinmuxChannelConsole:
87 *rx_pad = kDtPadIoc3;
88 *tx_pad = kDtPadIoc4;
89 break;
90 case kUartPinmuxChannelDut:
91 *rx_pad = kDtPadIob4;
92 *tx_pad = kDtPadIob5;
93 break;
94 default:
95 return INVALID_ARGUMENT();
96 }
97 }
98#else
99 return UNIMPLEMENTED();
100#endif
101
102 return OK_STATUS();
103}
104
105status_t uart_testutils_select_pinmux(const dif_pinmux_t *pinmux,
106 uint8_t uart_idx,
107 uart_pinmux_channel_t channel) {
108 TRY_CHECK(channel < kUartPinmuxChannelCount, "Channel out of bounds");
109
110 dt_uart_t uart_dt = get_uart_instance(uart_idx);
111 TRY_CHECK(uart_dt < kDtUartCount, "UART index out of bounds");
112
113 // Get peripheral I/O descriptions for RX and TX
114 dt_periph_io_t rx_periph_io = dt_uart_periph_io(uart_dt, kDtUartPeriphIoRx);
115 dt_periph_io_t tx_periph_io = dt_uart_periph_io(uart_dt, kDtUartPeriphIoTx);
116
117 // Get the appropriate pads for this UART instance and channel
118 dt_pad_t rx_pad, tx_pad;
119 TRY(get_uart_pads_for_channel(uart_dt, channel, &rx_pad, &tx_pad));
120
121 // Connect RX input using low-level pinmux functions
122 TRY(dif_pinmux_mio_select_input(pinmux, rx_periph_io, rx_pad));
123
124 // Connect TX output using low-level pinmux functions
125 TRY(dif_pinmux_mio_select_output(pinmux, tx_pad, tx_periph_io));
126
127 return OK_STATUS();
128}
129
130status_t uart_testutils_detach_pinmux(const dif_pinmux_t *pinmux,
131 uint8_t uart_idx) {
132 dt_uart_t uart_dt = get_uart_instance(uart_idx);
133 TRY_CHECK(uart_dt < kDtUartCount, "UART index out of bounds");
134
135 // Get peripheral I/O description for RX
136 dt_periph_io_t rx_periph_io = dt_uart_periph_io(uart_dt, kDtUartPeriphIoRx);
137
138 // Disconnect RX input by connecting to constant zero using low-level function
139 TRY(dif_pinmux_mio_select_input(pinmux, rx_periph_io, kDtPadConstantZero));
140
141 return OK_STATUS();
142}