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 
18 static const dt_gpio_t kGpioDt = kDtGpio;
19 static const dt_uart_t kUart0Dt = kDtUart0;
20 
21 #if defined(OPENTITAN_IS_EARLGREY) || defined(OPENTITAN_IS_ENGLISHBREAKFAST)
22 static const dt_pad_t kPadUart0Tx = kDtPadIoc4;
23 static const dt_pad_t kPadUart0Rx = kDtPadIoc3;
24 #define HAS_UART1
25 static const dt_uart_t kUart1Dt = kDtUart1;
26 static const dt_pad_t kPadUart1Tx = kDtPadIob5;
27 static const dt_pad_t kPadUart1Rx = kDtPadIob4;
28 static const dt_pad_t kPadStrap0 = kDtPadIoc0;
29 static const dt_pad_t kPadStrap1 = kDtPadIoc1;
30 static const dt_pad_t kPadStrap2 = kDtPadIoc2;
31 
32 #elif defined(OPENTITAN_IS_DARJEELING)
33 static const dt_pad_t kPadUart0Tx = kDtPadUart0Tx;
34 static const dt_pad_t kPadUart0Rx = kDtPadUart0Rx;
35 /* No UART1 */
36 static const dt_pad_t kPadStrap0 = kDtPadGpioGpio22;
37 static const dt_pad_t kPadStrap1 = kDtPadGpioGpio23;
38 static const dt_pad_t kPadStrap2 = kDtPadGpioGpio24;
39 
40 #else /* OPENTITAN_IS_* */
41 #error Unsupported top
42 #endif /* OPENTITAN_IS_* */
43 
44 void 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.
68  if (kDeviceType == kDeviceSimDV) {
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 
121 status_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)) {
125  case kDtPeriphIoTypeMio:
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();
143  case kDtPeriphIoTypeDio:
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)
172 const dt_pad_t kPinmuxTestutilsGpioPads[kDifGpioNumPins] = {
173  kDtPadIoa0, kDtPadIoa1, kDtPadIoa2, kDtPadIoa3, kDtPadIoa4,
174  kDtPadIoa5, kDtPadIoa6, kDtPadIoa7, kDtPadIoa8, kDtPadIob6,
175  kDtPadIob7, kDtPadIob8, kDtPadIob9, kDtPadIob10, kDtPadIob11,
176  kDtPadIob12, kDtPadIoc9, kDtPadIoc10, kDtPadIoc11, kDtPadIoc12,
177  kDtPadIor0, kDtPadIor1, kDtPadIor2, kDtPadIor3, kDtPadIor4,
178  kDtPadIor5, kDtPadIor6, kDtPadIor7, kDtPadIor10, kDtPadIor11,
179  kDtPadIor12, kDtPadIor13};
180 #elif defined(OPENTITAN_IS_DARJEELING)
181 const dt_pad_t kPinmuxTestutilsGpioPads[kDifGpioNumPins] = {
182  kDtPadGpioGpio0, kDtPadGpioGpio1, kDtPadGpioGpio2, kDtPadGpioGpio3,
183  kDtPadGpioGpio4, kDtPadGpioGpio5, kDtPadGpioGpio6, kDtPadGpioGpio7,
184  kDtPadGpioGpio8, kDtPadGpioGpio9, kDtPadGpioGpio10, kDtPadGpioGpio11,
185  kDtPadGpioGpio12, kDtPadGpioGpio13, kDtPadGpioGpio14, kDtPadGpioGpio15,
186  kDtPadGpioGpio16, kDtPadGpioGpio17, kDtPadGpioGpio18, kDtPadGpioGpio19,
187  kDtPadGpioGpio20, kDtPadGpioGpio21, kDtPadGpioGpio22, kDtPadGpioGpio23,
188  kDtPadGpioGpio24, kDtPadGpioGpio25, kDtPadGpioGpio26, kDtPadGpioGpio27,
189  kDtPadGpioGpio28, kDtPadGpioGpio29, kDtPadGpioGpio30, kDtPadGpioGpio31,
190 };
191 #else /* OPENTITAN_IS_* */
192 #error Unsupported top
193 #endif /* OPENTITAN_IS_* */
194 
195 uint32_t pinmux_testutils_get_testable_gpios_mask(void) {
196  switch (kDeviceType) {
197  case kDeviceSimDV:
198  case kDeviceSimVerilator:
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 
213 uint32_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 
243 uint32_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 
251 void 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 }