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 dif_pwrmgr_request_sources_t wakeup_sources;
54 static uint8_t i2c_instance_under_test = 0;
55 static uint32_t i2c_clock_stretching_delay_micros = 0;
56 
57 /**
58  * External interrupt handler.
59  */
60 bool ottf_handle_irq(uint32_t *exc_info, dt_instance_id_t devid,
61  dif_rv_plic_irq_id_t irq_id) {
62  if (devid == dt_pwrmgr_instance_id(kPwrmgrDt) &&
63  irq_id == dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup)) {
64  CHECK_DIF_OK(dif_pwrmgr_irq_acknowledge(&pwrmgr, kDtPwrmgrIrqWakeup));
65  return true;
66  } else {
67  return false;
68  }
69 }
70 
71 static status_t reset_fifos(dif_i2c_t *i2c) {
72  TRY(dif_i2c_reset_rx_fifo(i2c));
73  TRY(dif_i2c_reset_tx_fifo(i2c));
74  TRY(dif_i2c_reset_fmt_fifo(i2c));
75  TRY(dif_i2c_reset_acq_fifo(i2c));
76  return OK_STATUS();
77 }
78 
79 static status_t i2c_detach_instance(dif_i2c_t *i2c, dif_pinmux_t *pinmux,
80  uint8_t i2c_instance) {
82  return i2c_testutils_detach_pinmux(pinmux, i2c_instance);
83 }
84 
85 static status_t i2c_configure_instance(dif_i2c_t *i2c, dif_pinmux_t *pinmux,
86  uint8_t i2c_instance) {
87  TRY_CHECK(i2c_instance < kDtI2cCount);
88 
89  dt_i2c_t dt_i2c = i2c_instance; // Starts at 0.
90  TRY(dif_i2c_init_from_dt(dt_i2c, i2c));
91 
92  TRY(i2c_testutils_select_pinmux(pinmux, i2c_instance,
93  I2cPinmuxPlatformIdHyper310));
94  TRY(i2c_testutils_set_speed(i2c, kDifI2cSpeedStandard));
96  return OK_STATUS();
97 }
98 
99 static status_t configure_device_address(ujson_t *uj, dif_i2c_t *i2c) {
100  i2c_target_address_t address;
101  TRY(UJSON_WITH_CRC(ujson_deserialize_i2c_target_address_t, uj, &address));
102 
105 
106  TRY(i2c_detach_instance(i2c, &pinmux, i2c_instance_under_test));
107  i2c_instance_under_test = address.instance;
108  TRY(i2c_configure_instance(i2c, &pinmux, i2c_instance_under_test));
109 
110  dif_i2c_id_t id0 = {
111  .mask = address.mask0,
112  .address = address.id0,
113  };
114  dif_i2c_id_t id1 = {
115  .mask = address.mask1,
116  .address = address.id1,
117  };
118  TRY(dif_i2c_set_device_id(i2c, &id0, &id1));
120  // After enabling target mode, reset all of the FIFOs to clear out
121  // any leftover junk.
122  TRY(reset_fifos(i2c));
123  return RESP_OK_STATUS(uj);
124 }
125 
126 static status_t wait_for_acq_fifo(dif_i2c_t *i2c, dif_i2c_level_t at_least,
127  const ibex_timeout_t *deadline) {
128  dif_i2c_level_t acq_fifo_lvl;
129  while (!ibex_timeout_check(deadline)) {
130  if (dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl) ==
131  kDifOk &&
132  acq_fifo_lvl >= at_least) {
133  return OK_STATUS();
134  }
135  }
136  return DEADLINE_EXCEEDED();
137 }
138 
139 static status_t recv_write_transfer(dif_i2c_t *i2c, i2c_transfer_start_t *txn,
140  uint32_t micros, uint32_t delay_micros) {
141  ibex_timeout_t deadline = ibex_timeout_init(micros);
142  uint8_t byte;
143  dif_i2c_signal_t signal;
144 
145  // Address phase.
146  TRY(wait_for_acq_fifo(i2c, 1, &deadline));
147  TRY(dif_i2c_acquire_byte(i2c, &byte, &signal));
148 
149  TRY_CHECK(signal == kDifI2cSignalStart || signal == kDifI2cSignalRepeat,
150  "Expected SignalStart(%u), got %u", kDifI2cSignalStart, signal);
151  txn->address = byte >> 1;
152 
153  // Data phase.
154  while (true) {
155  if (txn->length % 64 == 0 && delay_micros) {
156  busy_spin_micros(delay_micros);
157  }
158  TRY(wait_for_acq_fifo(i2c, 1, &deadline));
159  TRY(dif_i2c_acquire_byte(i2c, &byte, &signal));
160  if (signal != kDifI2cSignalNone) {
161  break;
162  }
163  txn->data[txn->length++] = byte;
164  }
165 
166  // End of transfer.
167  switch (signal) {
168  case kDifI2cSignalStop:
169  txn->stop = true;
170  return OK_STATUS(0);
171  case kDifI2cSignalRepeat:
172  txn->stop = false;
173  // Repeated start, return the address of the next operation.
174  return OK_STATUS(txn->address);
175  default:
176  return INTERNAL();
177  }
178 }
179 
180 static status_t start_read_transaction(ujson_t *uj, dif_i2c_t *i2c) {
181  i2c_transfer_start_t txn;
182  TRY(UJSON_WITH_CRC(ujson_deserialize_i2c_transfer_start_t, uj, &txn));
183  busy_spin_micros(i2c_clock_stretching_delay_micros);
184  TRY(i2c_testutils_target_read(i2c, txn.length, txn.data));
185  ibex_timeout_t deadline = ibex_timeout_init(kTestTimeout);
186  TRY(wait_for_acq_fifo(i2c, 2, &deadline));
187  uint8_t address = 0;
188  TRY(i2c_testutils_target_check_read(i2c, &address, NULL));
189  TRY_CHECK(txn.address == address, "Address read (%x) is not the expected(%x)",
190  address, txn.address);
191  return RESP_OK_STATUS(uj);
192 }
193 
194 static status_t start_write_transaction(ujson_t *uj, dif_i2c_t *i2c,
195  uint32_t delay_micros) {
196  i2c_transfer_start_t txn = (i2c_transfer_start_t){0};
197  TRY(recv_write_transfer(i2c, &txn, kTestTimeout, delay_micros));
198  return RESP_OK(ujson_serialize_i2c_transfer_start_t, uj, &txn);
199 }
200 
201 static status_t start_write_read_transaction(ujson_t *uj, dif_i2c_t *i2c) {
202  i2c_transfer_start_t read_trans;
203  TRY(UJSON_WITH_CRC(ujson_deserialize_i2c_transfer_start_t, uj, &read_trans));
204  TRY(i2c_testutils_target_read(i2c, read_trans.length, read_trans.data));
205 
206  i2c_transfer_start_t write_trans = (i2c_transfer_start_t){0};
207  uint8_t address =
208  (uint8_t)TRY(recv_write_transfer(i2c, &write_trans, kTestTimeout, 0));
209  TRY_CHECK(write_trans.stop == false, "Stop bit not expected at this point.");
210  TRY_CHECK(read_trans.address == address,
211  "Address (0x%x) is not the expected(0x%x)", address,
212  read_trans.address);
213  ibex_timeout_t deadline = ibex_timeout_init(kTestTimeout);
214  TRY(wait_for_acq_fifo(i2c, 1, &deadline));
215 
216  return RESP_OK(ujson_serialize_i2c_transfer_start_t, uj, &write_trans);
217 }
218 
219 static status_t enter_sleep(ujson_t *uj, dif_i2c_t *i2c, bool normal) {
220  dif_pinmux_wakeup_config_t wakeup_cfg = {
222  .signal_filter = false,
223  .pad_type = kDifPinmuxPadKindMio,
224  .pad_select = kTopEarlgreyPinmuxInselIoa7,
225  };
226  TRY(dif_pinmux_wakeup_detector_enable(&pinmux, 0, wakeup_cfg));
227  dif_pwrmgr_domain_config_t pwrmgr_domain_cfg = 0;
228  if (normal) {
229  // Normal sleep wakes up from an interrupt, so enable the relevant sources.
230  // Enable all the AON interrupts used in this test.
231  dif_rv_plic_irq_id_t plic_id =
232  dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup);
233  rv_plic_testutils_irq_range_enable(&plic, kPlicTarget, plic_id, plic_id);
234  // Enable pwrmgr interrupt.
235  TRY(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled));
236 
237  // Configure the power domains for normal sleep.
238  TRY(dif_pwrmgr_get_domain_config(&pwrmgr, &pwrmgr_domain_cfg));
239  pwrmgr_domain_cfg |= kDifPwrmgrDomainOptionMainPowerInLowPower;
240  }
241 
242  TRY(pwrmgr_testutils_enable_low_power(&pwrmgr, wakeup_sources,
243  pwrmgr_domain_cfg));
244 
245  LOG_INFO("Going to sleep.");
247 
248  if (normal) {
249  LOG_INFO("Woke from sleep.");
250  reset_fifos(i2c);
251  return RESP_OK_STATUS(uj);
252  } else {
253  // Deep sleep wakes up with a reset; we should never get here.
254  // A deep-sleep wakeup resets the chip, and we should detect that
255  // condition in `wakeup_check` before starting the command processor.
256  LOG_ERROR("Unexpected wake from deep sleep.");
257  return INTERNAL();
258  }
259 }
260 
261 static status_t wakeup_check(ujson_t *uj, dif_i2c_t *i2c) {
262  if (TRY(pwrmgr_testutils_is_wakeup_reason(&pwrmgr, wakeup_sources)) == 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  CHECK_DIF_OK(dif_pwrmgr_find_request_source(
323  &pwrmgr, kDifPwrmgrReqTypeWakeup, dt_pinmux_instance_id(kPinmuxDt),
324  kDtPinmuxWakeupPinWkupReq, &wakeup_sources));
325 
326  TRY(dif_rv_plic_init_from_dt(kRvPlicDt, &plic));
327  TRY(dif_rv_plic_reset(&plic));
328  // Enable global and external IRQ at Ibex.
329  irq_global_ctrl(true);
330  irq_external_ctrl(true);
331 
332  return OK_STATUS();
333 }
334 
335 bool test_main(void) {
336  CHECK_STATUS_OK(test_init());
337  CHECK_STATUS_OK(
338  i2c_configure_instance(&i2c, &pinmux, i2c_instance_under_test));
340  ujson_t uj = ujson_ottf_console();
341  status = command_processor(&uj);
342  LOG_INFO("status = %r", status);
343  return status_ok(status);
344 }