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