Software APIs
usbdev_pullup_test.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 // USB PULLUP test
6 //
7 // Test the driving of pull up resistor(s) that indicate the presence of usbdev
8 // on the USB, as well as its speed.
9 //
10 // 1. Check for the presence of VBUS.
11 // 2. Check the DP line is not high.
12 // 3. Assert the DP pull up, indicating the presence of a Full Speed device.
13 // 4. Check the DP line is high.
14 // 5. Deassert the DP pull up.
15 // [ Optionally
16 // 6. Enable pin flipping.
17 // 7. Assert the pull up again, now on the DN (Low Speed device to the host,
18 // unless pins flipped externally).
19 // 8. Check the DN line is high.
20 // 9. Deassert the pull up.
21 // ]
22 
23 #include <stdbool.h>
24 #include <stdint.h>
25 
26 #include "sw/device/lib/base/status.h"
32 #include "sw/device/lib/testing/pinmux_testutils.h"
33 #include "sw/device/lib/testing/test_framework/check.h"
35 
36 #define USBDEV_BASE_ADDR TOP_EARLGREY_USBDEV_BASE_ADDR
37 
38 /**
39  * Are we expecting VBUS to be low at the start of the test, ie. no connection
40  * to the physical host or the DPI model is inactive?
41  */
42 static const bool kCheckLowFirst = false;
43 
44 /**
45  * USB device handle
46  */
47 static dif_usbdev_t usbdev;
48 static dif_usbdev_buffer_pool_t buffer_pool;
49 
50 /**
51  * Pinmux handle
52  */
53 static dif_pinmux_t pinmux;
54 
55 // Set the usbdev configuration according to whether or not pin flipping is
56 // desired.
57 static status_t config_set(bool pinflip) {
58  dif_usbdev_config_t config = {
60  .use_tx_d_se0 = kDifToggleDisabled,
61  .single_bit_eop = kDifToggleDisabled,
62  .pin_flip = dif_bool_to_toggle(pinflip),
63  .clock_sync_signals = kDifToggleEnabled,
64  };
65 
66  TRY(dif_usbdev_configure(&usbdev, &buffer_pool, config));
67 
68  return OK_STATUS();
69 }
70 
71 // Wait with timeout until the VBUS/SENSE signal is in the expected state.
72 static status_t vbus_wait(bool expected, uint32_t timeout_micros) {
73  ibex_timeout_t timeout = ibex_timeout_init(timeout_micros);
74  do {
75  // Read the current state of VBUS.
76  bool vbus;
77  TRY(dif_usbdev_status_get_sense(&usbdev, &vbus));
78  if (vbus == expected) {
79  return OK_STATUS();
80  }
81  } while (!ibex_timeout_check(&timeout));
82 
83  return UNAVAILABLE();
84 }
85 
86 // Wait with timeout for the specified USB data line to be in the expected
87 // state.
88 static status_t line_wait(bool dp, bool expected, uint32_t timeout_micros) {
89  ibex_timeout_t timeout = ibex_timeout_init(timeout_micros);
90  do {
91  // Sense the current state of the pins.
93  TRY(dif_usbdev_get_phy_pins_status(&usbdev, &status));
94 
95  if ((dp && status.rx_dp == expected) || (!dp && status.rx_dn == expected)) {
96  return OK_STATUS();
97  }
98  } while (!ibex_timeout_check(&timeout));
99 
100  return UNAVAILABLE();
101 }
102 
103 // Delay for the specified number of microseconds, with user reporting for
104 // appropriate targets.
105 static status_t delay(bool prompt, uint32_t timeout_micros) {
106  if (prompt) {
107  LOG_INFO("Delaying...");
108  }
109  busy_spin_micros(timeout_micros);
110 
111  return OK_STATUS();
112 }
113 
114 OTTF_DEFINE_TEST_CONFIG();
115 
116 bool test_main(void) {
117  // In simulation the DPI model connects VBUS shortly after reset and
118  // prolonged delays when asserting or deasserting pull ups are wasteful.
119  uint32_t timeout_micros = 1000u;
120  uint32_t delay_micros = 1u;
121  bool can_flip = true;
122  bool prompt = false;
123 
125  // FPGA platforms where user intervention may be required.
126  timeout_micros = 30 * 1000 * 1000u;
127  // A short delay here permits the activity of the host controller to be
128  // observed (eg. dmesg -w on a Linux host).
129  delay_micros = 2 * 1000 * 1000u;
130  // The CW310/340 board and their FPGA builds cannot raise the DN pull up
131  // because the required resistor is not mounted by default.
132  can_flip = false;
133  // Report instructions/progress to user, when driven manually.
134  prompt = true;
135  }
136 
137  // Ensure that the VBUS/SENSE signal is routed through to the usbdev.
138  CHECK_DIF_OK(dif_pinmux_init(
140  pinmux_testutils_init(&pinmux);
141  CHECK_DIF_OK(dif_pinmux_input_select(
144 
145  // DP line first (Full Speed device), and for some targets the only line that
146  // has a pull up to be tested.
147  bool dp = true;
148  do {
149  // Initialize and configure the usbdev with pin flipping set appropriately.
150  CHECK_DIF_OK(
151  dif_usbdev_init(mmio_region_from_addr(USBDEV_BASE_ADDR), &usbdev));
152  CHECK_STATUS_OK(config_set(!dp));
153 
154  // Initially the VBUS may be expected to be low; if so, ensure that this is
155  // the case.
156  if (kCheckLowFirst) {
157  if (prompt) {
158  bool vbus;
159  CHECK_DIF_OK(dif_usbdev_status_get_sense(&usbdev, &vbus));
160  if (vbus) {
161  LOG_INFO("Disconnect or power down the USB");
162  }
163  }
164 
165  CHECK_STATUS_OK(vbus_wait(false, timeout_micros));
166 
167  if (prompt) {
168  LOG_INFO("Connect or power up the USB");
169  }
170  }
171 
172  // Check for VBUS present/risen.
173  CHECK_STATUS_OK(vbus_wait(true, timeout_micros));
174 
175  // Check the Dx line is low.
176  CHECK_STATUS_OK(line_wait(dp, false, 1000u));
177 
178  // Delay a little, mostly to slow things on user-driven FPGA for
179  // observation.
180  CHECK_STATUS_OK(delay(prompt, delay_micros));
181 
182  // Assert the Dx pull up, indicating the presence of a Full Speed device.
183  CHECK_DIF_OK(dif_usbdev_interface_enable(&usbdev, kDifToggleEnabled));
184 
185  // Check the Dx line has risen.
186  CHECK_STATUS_OK(line_wait(dp, true, 1000u));
187 
188  // Delay a while so that the activity of the host in detecting and
189  // attempting to configure the device may be observed as additional
190  // confirmation.
191  CHECK_STATUS_OK(delay(prompt, delay_micros));
192 
193  // Deassert the pull up, disconnect us from the bus.
194  CHECK_DIF_OK(dif_usbdev_interface_enable(&usbdev, kDifToggleDisabled));
195 
196  // Dx line should drop in response.
197  CHECK_STATUS_OK(line_wait(dp, false, 1000u));
198 
199  // Try again with the other line.
200  dp = !dp;
201  } while (can_flip && !dp);
202 
203  return true;
204 }