Software APIs
i2c_target_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 #include "sw/device/lib/testing/json/i2c_target.h"
5 
6 #include <assert.h>
7 
17 #include "sw/device/lib/runtime/irq.h"
20 #include "sw/device/lib/testing/i2c_testutils.h"
21 #include "sw/device/lib/testing/json/command.h"
22 #include "sw/device/lib/testing/pwrmgr_testutils.h"
23 #include "sw/device/lib/testing/rv_plic_testutils.h"
24 #include "sw/device/lib/testing/test_framework/check.h"
25 #include "sw/device/lib/testing/test_framework/ottf_console.h"
27 #include "sw/device/lib/testing/test_framework/ujson_ottf.h"
28 #include "sw/device/lib/ujson/ujson.h"
29 
30 static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__,
31  "This test assumes the target platform is little endian.");
32 
33 OTTF_DEFINE_TEST_CONFIG(.enable_uart_flow_control = true);
34 
35 enum {
36  kTestTimeout = 1000000, // 1 second
37  kTransactionDelay = 10000, // 10 ms
38  kPlicTarget = 0,
39 };
40 
41 static const dt_pwrmgr_t kPwrmgrDt = 0;
42 static_assert(kDtPwrmgrCount == 1, "this test expects a pwrmgr");
43 static const dt_rv_plic_t kRvPlicDt = 0;
44 static_assert(kDtRvPlicCount == 1, "this test expects exactly one rv_plic");
45 static const dt_pinmux_t kPinmuxDt = 0;
46 static_assert(kDtPinmuxCount == 1, "this test expects exactly one pinmux");
47 
48 static dif_pinmux_t pinmux;
49 static dif_pwrmgr_t pwrmgr;
50 static dif_rv_plic_t plic;
51 static dif_i2c_t i2c;
52 
53 static uint8_t i2c_instance_under_test = 0;
54 static uint32_t i2c_clock_stretching_delay_micros = 0;
55 
56 /**
57  * External interrupt handler.
58  */
59 bool ottf_handle_irq(uint32_t *exc_info, dt_instance_id_t devid,
60  dif_rv_plic_irq_id_t irq_id) {
61  if (devid == dt_pwrmgr_instance_id(kPwrmgrDt) &&
62  irq_id == dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup)) {
63  CHECK_DIF_OK(dif_pwrmgr_irq_acknowledge(&pwrmgr, kDtPwrmgrIrqWakeup));
64  return true;
65  } else {
66  return false;
67  }
68 }
69 
70 static status_t reset_fifos(dif_i2c_t *i2c) {
71  TRY(dif_i2c_reset_rx_fifo(i2c));
72  TRY(dif_i2c_reset_tx_fifo(i2c));
73  TRY(dif_i2c_reset_fmt_fifo(i2c));
74  TRY(dif_i2c_reset_acq_fifo(i2c));
75  return OK_STATUS();
76 }
77 
78 static status_t i2c_detach_instance(dif_i2c_t *i2c, dif_pinmux_t *pinmux,
79  uint8_t i2c_instance) {
81  return i2c_testutils_detach_pinmux(pinmux, i2c_instance);
82 }
83 
84 static status_t i2c_configure_instance(dif_i2c_t *i2c, dif_pinmux_t *pinmux,
85  uint8_t i2c_instance) {
86  TRY_CHECK(i2c_instance < kDtI2cCount);
87 
88  dt_i2c_t dt_i2c = i2c_instance; // Starts at 0.
89  TRY(dif_i2c_init_from_dt(dt_i2c, i2c));
90 
91  TRY(i2c_testutils_select_pinmux(pinmux, i2c_instance,
92  I2cPinmuxPlatformIdHyper310));
93  TRY(i2c_testutils_set_speed(i2c, kDifI2cSpeedStandard));
95  return OK_STATUS();
96 }
97 
98 static status_t configure_device_address(ujson_t *uj, dif_i2c_t *i2c) {
99  i2c_target_address_t address;
100  TRY(UJSON_WITH_CRC(ujson_deserialize_i2c_target_address_t, uj, &address));
101 
104 
105  TRY(i2c_detach_instance(i2c, &pinmux, i2c_instance_under_test));
106  i2c_instance_under_test = address.instance;
107  TRY(i2c_configure_instance(i2c, &pinmux, i2c_instance_under_test));
108 
109  dif_i2c_id_t id0 = {
110  .mask = address.mask0,
111  .address = address.id0,
112  };
113  dif_i2c_id_t id1 = {
114  .mask = address.mask1,
115  .address = address.id1,
116  };
117  TRY(dif_i2c_set_device_id(i2c, &id0, &id1));
119  // After enabling target mode, reset all of the FIFOs to clear out
120  // any leftover junk.
121  TRY(reset_fifos(i2c));
122  return RESP_OK_STATUS(uj);
123 }
124 
125 static status_t wait_for_acq_fifo(dif_i2c_t *i2c, dif_i2c_level_t at_least,
126  const ibex_timeout_t *deadline) {
127  dif_i2c_level_t acq_fifo_lvl;
128  while (!ibex_timeout_check(deadline)) {
129  if (dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl) ==
130  kDifOk &&
131  acq_fifo_lvl >= at_least) {
132  return OK_STATUS();
133  }
134  }
135  return DEADLINE_EXCEEDED();
136 }
137 
138 static status_t recv_write_transfer(dif_i2c_t *i2c, i2c_transfer_start_t *txn,
139  uint32_t micros, uint32_t delay_micros) {
140  ibex_timeout_t deadline = ibex_timeout_init(micros);
141  uint8_t byte;
142  dif_i2c_signal_t signal;
143 
144  // Address phase.
145  TRY(wait_for_acq_fifo(i2c, 1, &deadline));
146  TRY(dif_i2c_acquire_byte(i2c, &byte, &signal));
147 
148  TRY_CHECK(signal == kDifI2cSignalStart || signal == kDifI2cSignalRepeat,
149  "Expected SignalStart(%u), got %u", kDifI2cSignalStart, signal);
150  txn->address = byte >> 1;
151 
152  // Data phase.
153  while (true) {
154  if (txn->length % 64 == 0 && delay_micros) {
155  busy_spin_micros(delay_micros);
156  }
157  TRY(wait_for_acq_fifo(i2c, 1, &deadline));
158  TRY(dif_i2c_acquire_byte(i2c, &byte, &signal));
159  if (signal != kDifI2cSignalNone) {
160  break;
161  }
162  txn->data[txn->length++] = byte;
163  }
164 
165  // End of transfer.
166  switch (signal) {
167  case kDifI2cSignalStop:
168  txn->stop = true;
169  return OK_STATUS(0);
170  case kDifI2cSignalRepeat:
171  txn->stop = false;
172  // Repeated start, return the address of the next operation.
173  return OK_STATUS(txn->address);
174  default:
175  return INTERNAL();
176  }
177 }
178 
179 static status_t start_read_transaction(ujson_t *uj, dif_i2c_t *i2c) {
180  i2c_transfer_start_t txn;
181  TRY(UJSON_WITH_CRC(ujson_deserialize_i2c_transfer_start_t, uj, &txn));
182  busy_spin_micros(i2c_clock_stretching_delay_micros);
183  TRY(i2c_testutils_target_read(i2c, txn.length, txn.data));
184  ibex_timeout_t deadline = ibex_timeout_init(kTestTimeout);
185  TRY(wait_for_acq_fifo(i2c, 2, &deadline));
186  uint8_t address = 0;
187  TRY(i2c_testutils_target_check_read(i2c, &address, NULL));
188  TRY_CHECK(txn.address == address, "Address read (%x) is not the expected(%x)",
189  address, txn.address);
190  return RESP_OK_STATUS(uj);
191 }
192 
193 static status_t start_write_transaction(ujson_t *uj, dif_i2c_t *i2c,
194  uint32_t delay_micros) {
195  i2c_transfer_start_t txn = (i2c_transfer_start_t){0};
196  TRY(recv_write_transfer(i2c, &txn, kTestTimeout, delay_micros));
197  return RESP_OK(ujson_serialize_i2c_transfer_start_t, uj, &txn);
198 }
199 
200 static status_t start_write_read_transaction(ujson_t *uj, dif_i2c_t *i2c) {
201  i2c_transfer_start_t read_trans;
202  TRY(UJSON_WITH_CRC(ujson_deserialize_i2c_transfer_start_t, uj, &read_trans));
203  TRY(i2c_testutils_target_read(i2c, read_trans.length, read_trans.data));
204 
205  i2c_transfer_start_t write_trans = (i2c_transfer_start_t){0};
206  uint8_t address =
207  (uint8_t)TRY(recv_write_transfer(i2c, &write_trans, kTestTimeout, 0));
208  TRY_CHECK(write_trans.stop == false, "Stop bit not expected at this point.");
209  TRY_CHECK(read_trans.address == address,
210  "Address (0x%x) is not the expected(0x%x)", address,
211  read_trans.address);
212  ibex_timeout_t deadline = ibex_timeout_init(kTestTimeout);
213  TRY(wait_for_acq_fifo(i2c, 1, &deadline));
214 
215  return RESP_OK(ujson_serialize_i2c_transfer_start_t, uj, &write_trans);
216 }
217 
218 static status_t enter_sleep(ujson_t *uj, dif_i2c_t *i2c, bool normal) {
219  dif_pinmux_wakeup_config_t wakeup_cfg = {
221  .signal_filter = false,
222  .pad_type = kDifPinmuxPadKindMio,
223  .pad_select = kTopEarlgreyPinmuxInselIoa7,
224  };
225  TRY(dif_pinmux_wakeup_detector_enable(&pinmux, 0, wakeup_cfg));
226  dif_pwrmgr_domain_config_t pwrmgr_domain_cfg = 0;
227  if (normal) {
228  // Normal sleep wakes up from an interrupt, so enable the relevant sources.
229  // Enable all the AON interrupts used in this test.
230  dif_rv_plic_irq_id_t plic_id =
231  dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup);
232  rv_plic_testutils_irq_range_enable(&plic, kPlicTarget, plic_id, plic_id);
233  // Enable pwrmgr interrupt.
234  TRY(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled));
235 
236  // Configure the power domains for normal sleep.
237  TRY(dif_pwrmgr_get_domain_config(&pwrmgr, &pwrmgr_domain_cfg));
238  pwrmgr_domain_cfg |= kDifPwrmgrDomainOptionMainPowerInLowPower;
239  }
240 
241  TRY(pwrmgr_testutils_enable_low_power(
242  &pwrmgr, kDifPwrmgrWakeupRequestSourceThree, pwrmgr_domain_cfg));
243 
244  LOG_INFO("Going to sleep.");
246 
247  if (normal) {
248  LOG_INFO("Woke from sleep.");
249  reset_fifos(i2c);
250  return RESP_OK_STATUS(uj);
251  } else {
252  // Deep sleep wakes up with a reset; we should never get here.
253  // A deep-sleep wakeup resets the chip, and we should detect that
254  // condition in `wakeup_check` before starting the command processor.
255  LOG_ERROR("Unexpected wake from deep sleep.");
256  return INTERNAL();
257  }
258 }
259 
260 static status_t wakeup_check(ujson_t *uj, dif_i2c_t *i2c) {
261  if (TRY(pwrmgr_testutils_is_wakeup_reason(
262  &pwrmgr, kDifPwrmgrWakeupRequestSourceThree)) == true) {
263  LOG_ERROR("Woke from deep sleep; resuming test.");
264  TRY(dif_pinmux_wakeup_detector_disable(&pinmux, 0));
265  // If we get here, the test harness is expecting to receive a
266  // status_t to know we woke back up.
267  return RESP_OK_STATUS(uj);
268  }
269  return OK_STATUS();
270 }
271 
272 static status_t command_processor(ujson_t *uj) {
273  TRY(wakeup_check(uj, &i2c));
274  while (true) {
275  test_command_t command;
276  TRY(UJSON_WITH_CRC(ujson_deserialize_test_command_t, uj, &command));
277  switch (command) {
278  case kTestCommandEnterNormalSleep:
279  RESP_ERR(uj, enter_sleep(uj, &i2c, true));
280  break;
281  case kTestCommandEnterDeepSleep:
282  RESP_ERR(uj, enter_sleep(uj, &i2c, false));
283  break;
284  case kTestCommandI2cTargetAddress:
285  RESP_ERR(uj, configure_device_address(uj, &i2c));
286  break;
287  case kTestCommandI2cStartTransferWrite:
288  RESP_ERR(uj, start_write_transaction(uj, &i2c, 0));
289  break;
290  case kTestCommandI2cStartTransferRead:
291  RESP_ERR(uj, start_read_transaction(uj, &i2c));
292  break;
293  case kTestCommandI2cStartTransferWriteRead:
294  RESP_ERR(uj, start_write_read_transaction(uj, &i2c));
295  break;
296  case kTestCommandI2cStartTransferWriteSlow:
297  // We'll insert a 10ms delay every 64 bytes, which is a huge delay
298  // at 100 KHz, forcing the peripheral to stretch the clock.
299  RESP_ERR(uj, start_write_transaction(uj, &i2c, kTransactionDelay));
300  break;
301  case kTestCommandI2cTestConfig: {
302  i2c_test_config_t config;
303  TRY(UJSON_WITH_CRC(ujson_deserialize_i2c_test_config_t, uj, &config));
304  i2c_clock_stretching_delay_micros =
305  config.clock_stretching_delay_millis * 1000;
306  RESP_ERR(uj, RESP_OK_STATUS(uj));
307  } break;
308  default:
309  LOG_ERROR("Unrecognized command: %d", command);
310  RESP_ERR(uj, INVALID_ARGUMENT());
311  }
312  }
313  // We should never reach here.
314  return INTERNAL();
315 }
316 
317 static status_t test_init(void) {
318  TRY(dif_pinmux_init_from_dt(kPinmuxDt, &pinmux));
319 
320  TRY(dif_pwrmgr_init_from_dt(kPwrmgrDt, &pwrmgr));
321 
322  TRY(dif_rv_plic_init_from_dt(kRvPlicDt, &plic));
323  TRY(dif_rv_plic_reset(&plic));
324  // Enable global and external IRQ at Ibex.
325  irq_global_ctrl(true);
326  irq_external_ctrl(true);
327 
328  return OK_STATUS();
329 }
330 
331 bool test_main(void) {
332  CHECK_STATUS_OK(test_init());
333  CHECK_STATUS_OK(
334  i2c_configure_instance(&i2c, &pinmux, i2c_instance_under_test));
336  ujson_t uj = ujson_ottf_console();
337  status = command_processor(&uj);
338  LOG_INFO("status = %r", status);
339  return status_ok(status);
340 }