Software APIs
i2c_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/i2c_testutils.h"
6
7#include <assert.h>
8#include <stdbool.h>
9#include <stdint.h>
10
11#include "hw/top/dt/dt_i2c.h"
12#include "hw/top/dt/dt_pinmux.h"
19#include "sw/device/lib/testing/pinmux_testutils.h"
20#include "sw/device/lib/testing/test_framework/check.h"
21
22#include "hw/top/i2c_regs.h" // Generated.
23
24#define MODULE_ID MAKE_MODULE_ID('i', 'i', 't')
25
26enum {
27 kI2cWrite = 0,
28 kI2cRead = 1,
29};
30
31// Default flags for i2c operations.
32static const dif_i2c_fmt_flags_t kDefaultFlags = {.start = false,
33 .stop = false,
34 .read = false,
35 .read_cont = false,
36 .suppress_nak_irq = false};
37
38/**
39 * Get the I2C instance from index.
40 *
41 * @param i2c_idx I2C index (0-based).
42 * @return I2C DT instance, or kDtI2cCount if invalid.
43 */
44static dt_i2c_t get_i2c_instance(uint8_t i2c_idx) {
45 if (i2c_idx >= kDtI2cCount) {
46 return kDtI2cCount;
47 }
48 return (dt_i2c_t)i2c_idx;
49}
50
51/**
52 * Get the appropriate pads for an I2C instance and platform based on the
53 * platform. This replicates the original behavior with different mappings for
54 * different platforms.
55 *
56 * @param i2c_dt I2C DT instance.
57 * @param platform The platform to connect the I2C to.
58 * @param sda_pad Output parameter for SDA pad.
59 * @param scl_pad Output parameter for SCL pad.
60 * @return OK_STATUS if successful, error status otherwise.
61 */
62static status_t get_i2c_pads_for_platform(dt_i2c_t i2c_dt,
63 i2c_pinmux_platform_id_t platform,
64 dt_pad_t *sda_pad,
65 dt_pad_t *scl_pad) {
66#if defined(OPENTITAN_IS_DARJEELING)
67 // Darjeeling only has I2C0 and uses dedicated pads
68 if (i2c_dt != kDtI2c0) {
69 return INVALID_ARGUMENT();
70 }
71 *sda_pad = kDtPadI2c0Sda;
72 *scl_pad = kDtPadI2c0Scl;
73#elif defined(OPENTITAN_IS_EARLGREY) || defined(OPENTITAN_IS_ENGLISHBREAKFAST)
74 // For Earlgrey and EnglishBreakfast platforms
75 switch (platform) {
76 case I2cPinmuxPlatformIdHyper310: // CW310 HyperDebug
77 *sda_pad = kDtPadIoa7;
78 *scl_pad = kDtPadIoa8;
79 break;
80 case I2cPinmuxPlatformIdDvsim: // DV
81 // In DV, there's one agent for each I2C instance, with a fixed set of
82 // muxed pins.
83 switch (i2c_dt) {
84 case kDtI2c0: // I2C0 uses the same pins as CW310 HyperDebug
85 *sda_pad = kDtPadIoa7;
86 *scl_pad = kDtPadIoa8;
87 break;
88 case kDtI2c1:
89 *sda_pad = kDtPadIob10;
90 *scl_pad = kDtPadIob9;
91 break;
92 case kDtI2c2: // I2C2 uses the same pins as CW310 PMOD
93 *sda_pad = kDtPadIob12;
94 *scl_pad = kDtPadIob11;
95 break;
96 default:
97 return INVALID_ARGUMENT();
98 }
99 break;
100 case I2cPinmuxPlatformIdCw310Pmod: // CW310 PMOD
101 *sda_pad = kDtPadIob12;
102 *scl_pad = kDtPadIob11;
103 break;
104 default:
105 return INVALID_ARGUMENT();
106 }
107#else
108 return UNIMPLEMENTED();
109#endif
110
111 return OK_STATUS();
112}
113
114status_t i2c_testutils_write(const dif_i2c_t *i2c, uint8_t addr,
115 size_t byte_count, const uint8_t *data,
116 bool skip_stop) {
117 dif_i2c_fmt_flags_t flags = kDefaultFlags;
118 uint8_t data_frame;
119
120 TRY_CHECK(byte_count > 0);
121
122 // First write the address.
123 flags.start = true;
124 data_frame = (uint8_t)(addr << 1) | (uint8_t)kI2cWrite;
125 TRY(dif_i2c_write_byte_raw(i2c, data_frame, flags));
126
127 // Once address phase is through, blast the rest as generic data.
128 flags = kDefaultFlags;
129 if (byte_count > 1) {
130 TRY(dif_i2c_write_bytes_raw(i2c, byte_count - 1, data, flags));
131 }
132 // Issue a stop for the last byte.
133 flags.stop = !skip_stop;
134 TRY(dif_i2c_write_byte_raw(i2c, data[byte_count - 1], flags));
135
136 // TODO: Check for errors / status.
137 return OK_STATUS();
138}
139
140status_t i2c_testutils_issue_read(const dif_i2c_t *i2c, uint8_t addr,
141 uint8_t byte_count) {
142 dif_i2c_fmt_flags_t flags = kDefaultFlags;
143 uint8_t data_frame;
144 // The current function doesn't check for space in the FIFOs
145
146 // First, issue a write the address transaction.
147 flags.start = true;
148 data_frame = (uint8_t)(addr << 1) | (uint8_t)kI2cRead;
149 TRY(dif_i2c_write_byte_raw(i2c, data_frame, flags));
150
151 TRY(i2c_testutils_wait_transaction_finish(i2c));
152 dif_i2c_controller_halt_events_t halt_events = {0};
153 TRY(dif_i2c_get_controller_halt_events(i2c, &halt_events));
154 if (!halt_events.nack_received) {
155 // We got an ack, schedule the read transaction.
156 flags = kDefaultFlags;
157 flags.read = true;
158 flags.stop = true;
159 // Inform the controller how many bytes to read overall.
160 TRY(dif_i2c_write_byte_raw(i2c, byte_count, flags));
161 } else {
162 // We got a nack, clear the irq and return. The caller may retry later.
163 TRY(dif_i2c_reset_fmt_fifo(i2c));
164 TRY(dif_i2c_clear_controller_halt_events(i2c, halt_events));
165 // Force Stop to be sent after the unexpected NACK.
166 TRY(dif_i2c_host_set_enabled(i2c, kDifToggleDisabled));
167 TRY(dif_i2c_host_set_enabled(i2c, kDifToggleEnabled));
168 }
169 return OK_STATUS(halt_events.nack_received);
170}
171
172status_t i2c_testutils_read(const dif_i2c_t *i2c, uint8_t addr,
173 size_t byte_count, uint8_t *data, size_t timeout) {
174 uint32_t nak_count = 0;
175 ibex_timeout_t timer = ibex_timeout_init(timeout);
176
177 // Make sure to start from a clean state.
178 dif_i2c_controller_halt_events_t halt_events = {0};
179 TRY(dif_i2c_get_controller_halt_events(i2c, &halt_events));
180 TRY(dif_i2c_clear_controller_halt_events(i2c, halt_events));
181 TRY(dif_i2c_reset_rx_fifo(i2c));
182 do {
183 // Per chunk, as many bytes as fit into the RX FIFO could be transferred.
184 // However, as the number of bytes has to be communicated to the target in
185 // one byte, UINT8_MAX is another upper limit.
186 size_t max_chunk =
187 I2C_PARAM_FIFO_DEPTH < UINT8_MAX ? I2C_PARAM_FIFO_DEPTH : UINT8_MAX;
188 uint8_t chunk = (uint8_t)(byte_count < max_chunk ? byte_count : max_chunk);
189
190 // Loop until we get an ACK from the device or a timeout.
191 while (TRY(i2c_testutils_issue_read(i2c, addr, chunk))) {
192 nak_count++;
193 if (ibex_timeout_check(&timer)) {
194 return DEADLINE_EXCEEDED();
195 }
196 }
197
198 if (chunk > 0) {
199 TRY(dif_i2c_read_bytes(i2c, chunk, data));
200 data += chunk;
201 byte_count -= chunk;
202 }
203 } while (byte_count > 0);
204
205 TRY(i2c_testutils_wait_transaction_finish(i2c));
206 return OK_STATUS((int32_t)nak_count);
207}
208
209status_t i2c_testutils_target_check_start(const dif_i2c_t *i2c, uint8_t *addr) {
210 dif_i2c_level_t acq_fifo_lvl;
211 TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
212 TRY_CHECK(acq_fifo_lvl > 1);
213
214 dif_i2c_signal_t signal;
215 uint8_t byte;
216 TRY(dif_i2c_acquire_byte(i2c, &byte, &signal));
217 // Check acq_fifo is as expected and write addr and continue
218 TRY_CHECK(signal == kDifI2cSignalStart);
219 *addr = byte >> 1;
220
221 return OK_STATUS(byte & kI2cRead);
222}
223
224status_t i2c_testutils_target_check_end(const dif_i2c_t *i2c,
225 uint8_t *cont_byte) {
226 dif_i2c_level_t acq_fifo_lvl;
227 TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
228 TRY_CHECK(acq_fifo_lvl >= 1);
229
230 dif_i2c_signal_t signal;
231 uint8_t byte;
232 TRY(dif_i2c_acquire_byte(i2c, &byte, &signal));
233 // Check transaction is terminated with a stop or a continue that the caller
234 // is prepared to handle
235 if (signal == kDifI2cSignalStop) {
236 return OK_STATUS(false);
237 }
238 TRY_CHECK(cont_byte != NULL);
239 *cont_byte = byte;
240 TRY_CHECK(signal == kDifI2cSignalRepeat);
241
242 return OK_STATUS(true);
243}
244
245status_t i2c_testutils_target_read(const dif_i2c_t *i2c, uint16_t byte_count,
246 const uint8_t *data) {
247 dif_i2c_level_t tx_fifo_lvl, acq_fifo_lvl;
248 TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, &tx_fifo_lvl, &acq_fifo_lvl));
249 // Check there's space in tx_fifo and acq_fifo
250 TRY_CHECK(tx_fifo_lvl + byte_count <= I2C_PARAM_FIFO_DEPTH);
251 TRY_CHECK(acq_fifo_lvl + 2 <= I2C_PARAM_FIFO_DEPTH);
252
253 for (uint16_t i = 0; i < byte_count; ++i) {
254 TRY(dif_i2c_transmit_byte(i2c, data[i]));
255 }
256 // TODO: Check for errors / status.
257 return OK_STATUS();
258}
259
260status_t i2c_testutils_target_check_read(const dif_i2c_t *i2c, uint8_t *addr,
261 uint8_t *cont_byte) {
262 int32_t dir = TRY(i2c_testutils_target_check_start(i2c, addr));
263 TRY_CHECK(dir == kI2cRead);
264 // TODO: Check for errors / status.
265 return i2c_testutils_target_check_end(i2c, cont_byte);
266}
267
268status_t i2c_testutils_target_write(const dif_i2c_t *i2c, uint16_t byte_count) {
269 dif_i2c_level_t acq_fifo_lvl;
270 TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
271 TRY_CHECK(acq_fifo_lvl + 2 + byte_count <= I2C_PARAM_FIFO_DEPTH);
272
273 // TODO: Check for errors / status.
274 return OK_STATUS();
275}
276
277status_t i2c_testutils_target_check_write(const dif_i2c_t *i2c,
278 uint16_t byte_count, uint8_t *addr,
279 uint8_t *bytes, uint8_t *cont_byte) {
280 dif_i2c_level_t acq_fifo_lvl;
281 TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
282 TRY_CHECK(acq_fifo_lvl >= 2 + byte_count);
283
284 int32_t dir = TRY(i2c_testutils_target_check_start(i2c, addr));
285 TRY_CHECK(dir == kI2cWrite);
286
287 for (uint16_t i = 0; i < byte_count; ++i) {
288 dif_i2c_signal_t signal;
289 TRY(dif_i2c_acquire_byte(i2c, bytes + i, &signal));
290 TRY_CHECK(signal == kDifI2cSignalNone);
291 }
292
293 // TODO: Check for errors / status.
294
295 return i2c_testutils_target_check_end(i2c, cont_byte);
296}
297
298status_t i2c_testutils_select_pinmux(const dif_pinmux_t *pinmux, uint8_t i2c_id,
299 i2c_pinmux_platform_id_t platform) {
300 TRY_CHECK(platform < I2cPinmuxPlatformIdCount, "Platform out of bounds");
301
302 dt_i2c_t i2c_dt = get_i2c_instance(i2c_id);
303 TRY_CHECK(i2c_dt < kDtI2cCount, "I2C index out of bounds");
304
305 // Get peripheral I/O descriptions for SDA and SCL
306 dt_periph_io_t sda_periph_io = dt_i2c_periph_io(i2c_dt, kDtI2cPeriphIoSda);
307 dt_periph_io_t scl_periph_io = dt_i2c_periph_io(i2c_dt, kDtI2cPeriphIoScl);
308
309 // Get the appropriate pads for this I2C instance and platform
310 dt_pad_t sda_pad, scl_pad;
311 TRY(get_i2c_pads_for_platform(i2c_dt, platform, &sda_pad, &scl_pad));
312
313 // Connect SDA and SCL using pinmux testutils
314 TRY(pinmux_testutils_connect(pinmux, sda_periph_io, kDtPeriphIoDirInout,
315 sda_pad));
316 TRY(pinmux_testutils_connect(pinmux, scl_periph_io, kDtPeriphIoDirInout,
317 scl_pad));
318
319 return OK_STATUS();
320}
321
322status_t i2c_testutils_detach_pinmux(const dif_pinmux_t *pinmux,
323 uint8_t i2c_id) {
324 dt_i2c_t i2c_dt = get_i2c_instance(i2c_id);
325 TRY_CHECK(i2c_dt < kDtI2cCount, "I2C index out of bounds");
326
327 // Get peripheral I/O descriptions for SDA and SCL
328 dt_periph_io_t sda_periph_io = dt_i2c_periph_io(i2c_dt, kDtI2cPeriphIoSda);
329 dt_periph_io_t scl_periph_io = dt_i2c_periph_io(i2c_dt, kDtI2cPeriphIoScl);
330
331 // Disconnect SDA and SCL inputs by connecting to constant zero
332 TRY(pinmux_testutils_connect(pinmux, sda_periph_io, kDtPeriphIoDirIn,
334 TRY(pinmux_testutils_connect(pinmux, scl_periph_io, kDtPeriphIoDirIn,
336
337 return OK_STATUS();
338}
339
340status_t i2c_testutils_set_speed(const dif_i2c_t *i2c, dif_i2c_speed_t speed,
341 uint32_t sda_rise_nanos,
342 uint32_t sda_fall_nanos) {
343 uint32_t speed_khz = 0;
344 switch (speed) {
346 LOG_INFO("Setting i2c to %s mode.", "Standard (100kHz)");
347 speed_khz = 100;
348 break;
349 case kDifI2cSpeedFast:
350 LOG_INFO("Setting i2c to %s mode.", "Fast (400kHz)");
351 speed_khz = 400;
352 break;
354 LOG_INFO("Setting i2c to %s mode.", "FastPlus (1000kHz)");
355 speed_khz = 1000;
356 break;
357 default:
358 break;
359 }
360 // I2C speed parameters.
361 dif_i2c_timing_config_t timing_config = {
362 .lowest_target_device_speed = speed,
363 .clock_period_nanos =
364 (uint32_t)udiv64_slow(1000000000, kClockFreqPeripheralHz, NULL),
365 .sda_rise_nanos = sda_rise_nanos,
366 .sda_fall_nanos = sda_fall_nanos,
367 .scl_period_nanos = 1000000 / speed_khz};
368
370 TRY(dif_i2c_get_status(i2c, &status));
371 if (status.enable_host) {
372 TRY(dif_i2c_host_set_enabled(i2c, kDifToggleDisabled));
373 }
374 dif_i2c_config_t config;
375 TRY(dif_i2c_compute_timing(timing_config, &config));
376
377 LOG_INFO("period:%d nanos, cycles={fall=%d, rise=%d, hi=%d, lo=%d}",
378 timing_config.clock_period_nanos, config.fall_cycles,
379 config.rise_cycles, config.scl_time_high_cycles,
380 config.scl_time_low_cycles);
381
382 TRY(dif_i2c_configure(i2c, config));
383 // Reenable if it was enabled before.
384 if (status.enable_host) {
385 TRY(dif_i2c_host_set_enabled(i2c, kDifToggleEnabled));
386 }
387
388 return OK_STATUS();
389}
390
391status_t i2c_testutils_wait_host_idle(const dif_i2c_t *i2c) {
393 do {
394 TRY(dif_i2c_get_status(i2c, &status));
395 } while (!status.host_idle);
396 return OK_STATUS();
397}
398
399status_t i2c_testutils_wait_transaction_finish(const dif_i2c_t *i2c) {
401 bool controller_halted = false;
402 do {
403 TRY(dif_i2c_irq_is_pending(i2c, kDifI2cIrqControllerHalt,
404 &controller_halted));
405 if (controller_halted) {
406 return OK_STATUS(1);
407 }
408 TRY(dif_i2c_get_status(i2c, &status));
409 } while (!status.fmt_fifo_empty);
410 return OK_STATUS(0);
411}