Software APIs
pinmux_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/pinmux_testutils.h"
6
7#include "dt/dt_pinmux.h"
8#include "dt/dt_uart.h"
11#include "sw/device/lib/base/status.h"
16#include "sw/device/lib/testing/test_framework/check.h"
17
18static const dt_gpio_t kGpioDt = kDtGpio;
19static const dt_uart_t kUart0Dt = kDtUart0;
20
21#if defined(OPENTITAN_IS_EARLGREY) || defined(OPENTITAN_IS_ENGLISHBREAKFAST)
22static const dt_pad_t kPadUart0Tx = kDtPadIoc4;
23static const dt_pad_t kPadUart0Rx = kDtPadIoc3;
24#define HAS_UART1
25static const dt_uart_t kUart1Dt = kDtUart1;
26static const dt_pad_t kPadUart1Tx = kDtPadIob5;
27static const dt_pad_t kPadUart1Rx = kDtPadIob4;
28static const dt_pad_t kPadStrap0 = kDtPadIoc0;
29static const dt_pad_t kPadStrap1 = kDtPadIoc1;
30static const dt_pad_t kPadStrap2 = kDtPadIoc2;
31
32#elif defined(OPENTITAN_IS_DARJEELING)
33static const dt_pad_t kPadUart0Tx = kDtPadUart0Tx;
34static const dt_pad_t kPadUart0Rx = kDtPadUart0Rx;
35/* No UART1 */
36static const dt_pad_t kPadStrap0 = kDtPadGpioGpio22;
37static const dt_pad_t kPadStrap1 = kDtPadGpioGpio23;
38static const dt_pad_t kPadStrap2 = kDtPadGpioGpio24;
39
40#else /* OPENTITAN_IS_* */
41#error Unsupported top
42#endif /* OPENTITAN_IS_* */
43
44void pinmux_testutils_init(dif_pinmux_t *pinmux) {
45 // Set up SW straps on IOC0-IOC2, for GPIOs 22-24
46 CHECK_STATUS_OK(pinmux_testutils_connect(
47 pinmux, dt_gpio_periph_io(kGpioDt, kDtGpioPeriphIoGpio22),
48 kDtPeriphIoDirIn, kPadStrap0));
49 CHECK_STATUS_OK(pinmux_testutils_connect(
50 pinmux, dt_gpio_periph_io(kGpioDt, kDtGpioPeriphIoGpio23),
51 kDtPeriphIoDirIn, kPadStrap1));
52 CHECK_STATUS_OK(pinmux_testutils_connect(
53 pinmux, dt_gpio_periph_io(kGpioDt, kDtGpioPeriphIoGpio24),
54 kDtPeriphIoDirIn, kPadStrap2));
55
56 // Configure UART0 RX input.
57 CHECK_STATUS_OK(pinmux_testutils_connect(
58 pinmux, dt_uart_periph_io(kUart0Dt, kDtUartPeriphIoRx), kDtPeriphIoDirIn,
59 kPadUart0Rx));
60 // Configure UART0 TX output.
61 CHECK_STATUS_OK(pinmux_testutils_connect(
62 pinmux, dt_uart_periph_io(kUart0Dt, kDtUartPeriphIoTx), kDtPeriphIoDirOut,
63 kPadUart0Tx));
64
65#ifdef OPENTITAN_IS_EARLGREY
66 // Enable pull-ups on UART0 RX
67 // Pull-ups are available only on certain platforms.
69 dif_pinmux_pad_attr_t out_attr;
70 dif_pinmux_pad_attr_t in_attr = {
71 .slew_rate = 0,
72 .drive_strength = 0,
73 .flags = kDifPinmuxPadAttrPullResistorEnable |
74 kDifPinmuxPadAttrPullResistorUp};
75
76 CHECK_DIF_OK(
77 dif_pinmux_pad_write_attrs_dt(pinmux, kPadUart0Rx, in_attr, &out_attr));
78 };
79#endif
80
81#ifdef HAS_UART1
82 // Configure UART1 RX input.
83 CHECK_STATUS_OK(pinmux_testutils_connect(
84 pinmux, dt_uart_periph_io(kUart1Dt, kDtUartPeriphIoRx), kDtPeriphIoDirIn,
85 kPadUart1Rx));
86 // Configure UART1 TX output.
87 CHECK_STATUS_OK(pinmux_testutils_connect(
88 pinmux, dt_uart_periph_io(kUart1Dt, kDtUartPeriphIoTx), kDtPeriphIoDirOut,
89 kPadUart1Tx));
90#endif /* HAS_UART1 */
91
92#ifdef OPENTITAN_IS_EARLGREY
93 // Configure a higher drive strength for the USB_P and USB_N pads because
94 // the pad drivers must be capable of overpowering the 'pull' signal
95 // strength of the internal pull ups in the differential receiver.
96 //
97 // 'pull' strength is required because at the host end of the USB, there
98 // are 'weak' pull downs, allowing it to detect device presence when it
99 // applies its pull up.
100 // strong PAD driver > internal pull up > weak pull down at host
101 //
102 // Normally the pull up on USB_P will be asserted, but we may be employing
103 // 'pin flipping' and instead choose to apply the _N pull up.
104 if (kDeviceType == kDeviceSimDV) {
105 dif_pinmux_pad_attr_t out_attr;
106 dif_pinmux_pad_attr_t in_attr = {
107 .slew_rate = 0, .drive_strength = 1, .flags = 0};
108
109 CHECK_DIF_OK(dif_pinmux_pad_write_attrs_dt(pinmux, kDtPadUsbdevUsbDp,
110 in_attr, &out_attr));
111 CHECK_DIF_OK(dif_pinmux_pad_write_attrs_dt(pinmux, kDtPadUsbdevUsbDn,
112 in_attr, &out_attr));
113 }
114
115 // Configure USBDEV SENSE outputs to be high-Z (IOC7)
116 CHECK_DIF_OK(dif_pinmux_mio_select_output(pinmux, kDtPadIoc7,
117 kDtPeriphIoConstantHighZ));
118#endif /* OPENTITAN_IS_EARLGREY* */
119}
120
121status_t pinmux_testutils_connect(const dif_pinmux_t *pinmux,
122 dt_periph_io_t periph_io,
123 dt_periph_io_dir_t dir, dt_pad_t pad) {
124 switch (dt_periph_io_type(periph_io)) {
126 if (dt_pad_type(pad) != kDtPadTypeMio) {
127 return INVALID_ARGUMENT();
128 }
129 // Configure input.
130 if (dir == kDtPeriphIoDirIn || dir == kDtPeriphIoDirInout) {
131 TRY(dif_pinmux_mio_select_input(pinmux, periph_io, pad));
132 }
133 // Configure output as requested...
134 if (dir == kDtPeriphIoDirOut || dir == kDtPeriphIoDirInout) {
135 TRY(dif_pinmux_mio_select_output(pinmux, pad, periph_io));
136 }
137 // ... or as high-Z.
138 else if (dt_periph_io_dir(periph_io) == kDtPeriphIoDirInout) {
139 TRY(dif_pinmux_mio_select_output(pinmux, pad,
140 kDtPeriphIoConstantHighZ));
141 }
142 return OK_STATUS();
144 // Nothing to do but to check that they are actually connected together.
145 if (dt_pad_type(pad) != kDtPadTypeDio ||
146 dt_periph_io_dio_pad(periph_io) != pad) {
147 return INVALID_ARGUMENT();
148 }
149 // Make sure that the directions are compatible.
150 dt_periph_io_dir_t io_dir = dt_periph_io_dir(periph_io);
151 if ((io_dir == kDtPeriphIoDirIn || io_dir == kDtPeriphIoDirOut) &&
152 dir != io_dir) {
153 return INVALID_ARGUMENT();
154 }
155 return OK_STATUS();
156 default:
157 return INVALID_ARGUMENT();
158 }
159}
160
161// Mapping of Chip IOs to the GPIO peripheral.
162//
163// Depending on the simulation platform, there may be a limitation to how chip
164// IOs are allocated to the GPIO peripheral, even for testing. The DV testbench
165// does not have this limitation, and is able to allocate as many chip IOs as
166// the number of GPIOs supported by the peripheral. At this time, these pin
167// assignments matches DV (see `hw/top_earlgrey/dv/env/chip_if.sv`).
168//
169// The pinout spreadsheet allocates fewer pins to GPIOs than what the GPIO IP
170// supports. This oversubscription is intentional to maximize testing.
171#if defined(OPENTITAN_IS_EARLGREY) || defined(OPENTITAN_IS_ENGLISHBREAKFAST)
172const dt_pad_t kPinmuxTestutilsGpioPads[kDifGpioNumPins] = {
180#elif defined(OPENTITAN_IS_DARJEELING)
181const dt_pad_t kPinmuxTestutilsGpioPads[kDifGpioNumPins] = {
190};
191#else /* OPENTITAN_IS_* */
192#error Unsupported top
193#endif /* OPENTITAN_IS_* */
194
195uint32_t pinmux_testutils_get_testable_gpios_mask(void) {
196 switch (kDeviceType) {
197 case kDeviceSimDV:
199 // All GPIOs are testable in DV.
200 return 0xffffffff;
201 case kDeviceFpgaCw310:
202 // Only IOR6, IOR7, and IOR10 to IOR13 are available for use as GPIOs.
203 return 0xfc000000;
204 case kDeviceSilicon:
205 // IOA3/6, IOB6, IOC9-12, IOR5-7 and IOR10-13.
206 return 0xfe0f0248;
207 default:
208 CHECK(false);
209 return 0;
210 }
211}
212
213uint32_t pinmux_testutils_read_strap_pin(dif_pinmux_t *pinmux, dif_gpio_t *gpio,
214 dif_gpio_pin_t io, dt_pad_t pad) {
215 // Turn off the pull enable on the pad and read the IO.
216 dif_pinmux_pad_attr_t attr = {.flags = 0};
217 dif_pinmux_pad_attr_t attr_out;
218 CHECK_DIF_OK(dif_pinmux_pad_write_attrs_dt(pinmux, pad, attr, &attr_out));
219 // Let the change propagate.
220 busy_spin_micros(100);
221 bool state;
222 // The value read is unmodified by the internal pull resistors and represents
223 // the upper bit of the 4 possible states [Strong0, Weak0, Weak1,
224 // Strong1].
225 CHECK_DIF_OK(dif_gpio_read(gpio, io, &state));
226 uint32_t result = state ? 2 : 0;
227
228 // Based on the previous read, enable the opposite pull resistor. If the
229 // external signal is weak, the internal pull resistor will win; if the
230 // external signal is strong, the external value will win.
231 attr.flags = kDifPinmuxPadAttrPullResistorEnable |
232 (state ? 0 : kDifPinmuxPadAttrPullResistorUp);
233 CHECK_DIF_OK(dif_pinmux_pad_write_attrs_dt(pinmux, pad, attr, &attr_out));
234 // Let the change propagate.
235 busy_spin_micros(100);
236 // Combine the result of the contest between the external signal in internal
237 // pull resistors. This represents the lower bit of the 4 possible states.
238 CHECK_DIF_OK(dif_gpio_read(gpio, io, &state));
239 result += state ? 1 : 0;
240 return result;
241}
242
243uint32_t pinmux_testutils_read_straps(dif_pinmux_t *pinmux, dif_gpio_t *gpio) {
244 uint32_t strap = 0;
245 strap |= pinmux_testutils_read_strap_pin(pinmux, gpio, 22, kPadStrap0);
246 strap |= pinmux_testutils_read_strap_pin(pinmux, gpio, 23, kPadStrap1) << 2;
247 strap |= pinmux_testutils_read_strap_pin(pinmux, gpio, 24, kPadStrap2) << 4;
248 return strap;
249}
250
251void pinmux_testutils_configure_pads(const dif_pinmux_t *pinmux,
252 const pinmux_pad_attributes_t *attrs,
253 size_t num_attrs) {
254 for (size_t i = 0; i < num_attrs; ++i) {
255 dif_pinmux_pad_attr_t desired_attr, actual_attr;
256 CHECK_DIF_OK(
257 dif_pinmux_pad_get_attrs_dt(pinmux, attrs[i].pad, &desired_attr));
258 desired_attr.flags = attrs[i].flags;
259 CHECK_DIF_OK(dif_pinmux_pad_write_attrs_dt(pinmux, attrs[i].pad,
260 desired_attr, &actual_attr));
261 }
262}