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
17#include "sw/device/lib/testing/pinmux_testutils.h"
18#include "sw/device/lib/testing/test_framework/check.h"
19
21#include "i2c_regs.h" // Generated.
22
23#define MODULE_ID MAKE_MODULE_ID('i', 'i', 't')
24
25enum {
26 kI2cWrite = 0,
27 kI2cRead = 1,
28};
29
30// Default flags for i2c operations.
31static const dif_i2c_fmt_flags_t kDefaultFlags = {.start = false,
32 .stop = false,
33 .read = false,
34 .read_cont = false,
35 .suppress_nak_irq = false};
36
37/**
38 * Define an i2c pinmux configuration.
39 */
40typedef struct i2c_pinmux_pins {
41 pinmux_testutils_peripheral_pin_t sda;
42 pinmux_testutils_peripheral_pin_t scl;
43} i2c_pinmux_pins_t;
44
45/**
46 * Define an i2c pinmux configuration.
47 */
48typedef struct i2c_platform_pins {
49 pinmux_testutils_mio_pin_t sda;
50 pinmux_testutils_mio_pin_t scl;
51} i2c_platform_pins_t;
52
53/**
54 * This table store the pins of all i2c instances of Earlgrey.
55 * This is used to connect i2c instances to mio pins based on the platform.
56 */
57static const i2c_pinmux_pins_t kI2cPinmuxPins[] = {
58 // I2C0.
59 {.sda =
60 {
63 },
64 .scl =
65 {
68 }},
69 // I2C1.
70 {.sda =
71 {
74 },
75 .scl =
76 {
79 }},
80 // I2C2.
81 {.sda =
82 {
85 },
86 .scl =
87 {
90 }},
91};
92
93/**
94 * Map the combination of platform and I2C ID to pins for SCL and SDA.
95 */
96static status_t map_platform_to_pins(i2c_pinmux_platform_id_t platform,
97 uint8_t i2c_id,
98 i2c_platform_pins_t *pins) {
99 TRY_CHECK(pins != NULL);
100 switch (platform) {
101 case I2cPinmuxPlatformIdHyper310: // CW310 HyperDebug
102 *pins =
103 (i2c_platform_pins_t){.sda =
104 {
107 },
108 .scl = {
111 }};
112 break;
113 case I2cPinmuxPlatformIdDvsim: // DV
114 // In DV, there's one agent for each I2C instance, with a fixed set of
115 // muxed pins.
116 switch (i2c_id) {
117 case 0: // I2C0 uses the same pins as CW310 HyperDebug
118 TRY(map_platform_to_pins(I2cPinmuxPlatformIdHyper310, i2c_id, pins));
119 break;
120 case 1:
121 *pins = (i2c_platform_pins_t){
122 .sda =
123 {
126 },
127 .scl =
128 {
131 },
132 };
133 break;
134 case 2: // I2C2 uses the same pins as CW310 PMOD
135 TRY(map_platform_to_pins(I2cPinmuxPlatformIdCw310Pmod, i2c_id, pins));
136 break;
137 default:
138 TRY_CHECK(false, "invalid i2c_id: %0d", i2c_id);
139 break;
140 }
141 break;
142 case I2cPinmuxPlatformIdCw310Pmod: // CW310 PMOD
143 *pins = (i2c_platform_pins_t){
144 .sda =
145 {
148 },
149 .scl = {
152 }};
153 break;
154 default:
155 TRY_CHECK(false, "invalid platform: %0d", platform);
156 break;
157 }
158 return OK_STATUS();
159}
160
161status_t i2c_testutils_write(const dif_i2c_t *i2c, uint8_t addr,
162 size_t byte_count, const uint8_t *data,
163 bool skip_stop) {
164 dif_i2c_fmt_flags_t flags = kDefaultFlags;
165 uint8_t data_frame;
166
167 TRY_CHECK(byte_count > 0);
168
169 // First write the address.
170 flags.start = true;
171 data_frame = (uint8_t)(addr << 1) | (uint8_t)kI2cWrite;
172 TRY(dif_i2c_write_byte_raw(i2c, data_frame, flags));
173
174 // Once address phase is through, blast the rest as generic data.
175 flags = kDefaultFlags;
176 if (byte_count > 1) {
177 TRY(dif_i2c_write_bytes_raw(i2c, byte_count - 1, data, flags));
178 }
179 // Issue a stop for the last byte.
180 flags.stop = !skip_stop;
181 TRY(dif_i2c_write_byte_raw(i2c, data[byte_count - 1], flags));
182
183 // TODO: Check for errors / status.
184 return OK_STATUS();
185}
186
187status_t i2c_testutils_issue_read(const dif_i2c_t *i2c, uint8_t addr,
188 uint8_t byte_count) {
189 dif_i2c_fmt_flags_t flags = kDefaultFlags;
190 uint8_t data_frame;
191 // The current function doesn't check for space in the FIFOs
192
193 // First, issue a write the address transaction.
194 flags.start = true;
195 data_frame = (uint8_t)(addr << 1) | (uint8_t)kI2cRead;
196 TRY(dif_i2c_write_byte_raw(i2c, data_frame, flags));
197
198 TRY(i2c_testutils_wait_transaction_finish(i2c));
199 dif_i2c_controller_halt_events_t halt_events = {0};
200 TRY(dif_i2c_get_controller_halt_events(i2c, &halt_events));
201 if (!halt_events.nack_received) {
202 // We got an ack, schedule the read transaction.
203 flags = kDefaultFlags;
204 flags.read = true;
205 flags.stop = true;
206 // Inform the controller how many bytes to read overall.
207 TRY(dif_i2c_write_byte_raw(i2c, byte_count, flags));
208 } else {
209 // We got a nack, clear the irq and return. The caller may retry later.
210 TRY(dif_i2c_reset_fmt_fifo(i2c));
211 TRY(dif_i2c_clear_controller_halt_events(i2c, halt_events));
212 // Force Stop to be sent after the unexpected NACK.
213 TRY(dif_i2c_host_set_enabled(i2c, kDifToggleDisabled));
214 TRY(dif_i2c_host_set_enabled(i2c, kDifToggleEnabled));
215 }
216 return OK_STATUS(halt_events.nack_received);
217}
218
219status_t i2c_testutils_read(const dif_i2c_t *i2c, uint8_t addr,
220 size_t byte_count, uint8_t *data, size_t timeout) {
221 uint32_t nak_count = 0;
222 ibex_timeout_t timer = ibex_timeout_init(timeout);
223
224 // Make sure to start from a clean state.
225 dif_i2c_controller_halt_events_t halt_events = {0};
226 TRY(dif_i2c_get_controller_halt_events(i2c, &halt_events));
227 TRY(dif_i2c_clear_controller_halt_events(i2c, halt_events));
228 TRY(dif_i2c_reset_rx_fifo(i2c));
229 do {
230 // Per chunk, as many bytes as fit into the RX FIFO could be transferred.
231 // However, as the number of bytes has to be communicated to the target in
232 // one byte, UINT8_MAX is another upper limit.
233 size_t max_chunk =
234 I2C_PARAM_FIFO_DEPTH < UINT8_MAX ? I2C_PARAM_FIFO_DEPTH : UINT8_MAX;
235 uint8_t chunk = (uint8_t)(byte_count < max_chunk ? byte_count : max_chunk);
236
237 // Loop until we get an ACK from the device or a timeout.
238 while (TRY(i2c_testutils_issue_read(i2c, addr, chunk))) {
239 nak_count++;
240 if (ibex_timeout_check(&timer)) {
241 return DEADLINE_EXCEEDED();
242 }
243 }
244
245 if (chunk > 0) {
246 TRY(dif_i2c_read_bytes(i2c, chunk, data));
247 data += chunk;
248 byte_count -= chunk;
249 }
250 } while (byte_count > 0);
251
252 TRY(i2c_testutils_wait_transaction_finish(i2c));
253 return OK_STATUS((int32_t)nak_count);
254}
255
256status_t i2c_testutils_target_check_start(const dif_i2c_t *i2c, uint8_t *addr) {
257 dif_i2c_level_t acq_fifo_lvl;
258 TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
259 TRY_CHECK(acq_fifo_lvl > 1);
260
261 dif_i2c_signal_t signal;
262 uint8_t byte;
263 TRY(dif_i2c_acquire_byte(i2c, &byte, &signal));
264 // Check acq_fifo is as expected and write addr and continue
265 TRY_CHECK(signal == kDifI2cSignalStart);
266 *addr = byte >> 1;
267
268 return OK_STATUS(byte & kI2cRead);
269}
270
271status_t i2c_testutils_target_check_end(const dif_i2c_t *i2c,
272 uint8_t *cont_byte) {
273 dif_i2c_level_t acq_fifo_lvl;
274 TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
275 TRY_CHECK(acq_fifo_lvl >= 1);
276
277 dif_i2c_signal_t signal;
278 uint8_t byte;
279 TRY(dif_i2c_acquire_byte(i2c, &byte, &signal));
280 // Check transaction is terminated with a stop or a continue that the caller
281 // is prepared to handle
282 if (signal == kDifI2cSignalStop) {
283 return OK_STATUS(false);
284 }
285 TRY_CHECK(cont_byte != NULL);
286 *cont_byte = byte;
287 TRY_CHECK(signal == kDifI2cSignalRepeat);
288
289 return OK_STATUS(true);
290}
291
292status_t i2c_testutils_target_read(const dif_i2c_t *i2c, uint16_t byte_count,
293 const uint8_t *data) {
294 dif_i2c_level_t tx_fifo_lvl, acq_fifo_lvl;
295 TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, &tx_fifo_lvl, &acq_fifo_lvl));
296 // Check there's space in tx_fifo and acq_fifo
297 TRY_CHECK(tx_fifo_lvl + byte_count <= I2C_PARAM_FIFO_DEPTH);
298 TRY_CHECK(acq_fifo_lvl + 2 <= I2C_PARAM_FIFO_DEPTH);
299
300 for (uint16_t i = 0; i < byte_count; ++i) {
301 TRY(dif_i2c_transmit_byte(i2c, data[i]));
302 }
303 // TODO: Check for errors / status.
304 return OK_STATUS();
305}
306
307status_t i2c_testutils_target_check_read(const dif_i2c_t *i2c, uint8_t *addr,
308 uint8_t *cont_byte) {
309 int32_t dir = TRY(i2c_testutils_target_check_start(i2c, addr));
310 TRY_CHECK(dir == kI2cRead);
311 // TODO: Check for errors / status.
312 return i2c_testutils_target_check_end(i2c, cont_byte);
313}
314
315status_t i2c_testutils_target_write(const dif_i2c_t *i2c, uint16_t byte_count) {
316 dif_i2c_level_t acq_fifo_lvl;
317 TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
318 TRY_CHECK(acq_fifo_lvl + 2 + byte_count <= I2C_PARAM_FIFO_DEPTH);
319
320 // TODO: Check for errors / status.
321 return OK_STATUS();
322}
323
324status_t i2c_testutils_target_check_write(const dif_i2c_t *i2c,
325 uint16_t byte_count, uint8_t *addr,
326 uint8_t *bytes, uint8_t *cont_byte) {
327 dif_i2c_level_t acq_fifo_lvl;
328 TRY(dif_i2c_get_fifo_levels(i2c, NULL, NULL, NULL, &acq_fifo_lvl));
329 TRY_CHECK(acq_fifo_lvl >= 2 + byte_count);
330
331 int32_t dir = TRY(i2c_testutils_target_check_start(i2c, addr));
332 TRY_CHECK(dir == kI2cWrite);
333
334 for (uint16_t i = 0; i < byte_count; ++i) {
335 dif_i2c_signal_t signal;
336 TRY(dif_i2c_acquire_byte(i2c, bytes + i, &signal));
337 TRY_CHECK(signal == kDifI2cSignalNone);
338 }
339
340 // TODO: Check for errors / status.
341
342 return i2c_testutils_target_check_end(i2c, cont_byte);
343}
344
345status_t i2c_testutils_select_pinmux(const dif_pinmux_t *pinmux, uint8_t i2c_id,
346 i2c_pinmux_platform_id_t platform) {
347 TRY_CHECK(
348 platform < I2cPinmuxPlatformIdCount && i2c_id < ARRAYSIZE(kI2cPinmuxPins),
349 "Index out of bounds");
350 i2c_platform_pins_t platform_pins;
351 TRY(map_platform_to_pins(platform, i2c_id, &platform_pins));
352 // Configure sda pin.
353 TRY(dif_pinmux_input_select(pinmux, kI2cPinmuxPins[i2c_id].sda.peripheral_in,
354 platform_pins.sda.insel));
355 TRY(dif_pinmux_output_select(pinmux, platform_pins.sda.mio_out,
356 kI2cPinmuxPins[i2c_id].sda.outsel));
357
358 // Configure scl pin.
359 TRY(dif_pinmux_input_select(pinmux, kI2cPinmuxPins[i2c_id].scl.peripheral_in,
360 platform_pins.scl.insel));
361 TRY(dif_pinmux_output_select(pinmux, platform_pins.scl.mio_out,
362 kI2cPinmuxPins[i2c_id].scl.outsel));
363 return OK_STATUS();
364}
365
366status_t i2c_testutils_detach_pinmux(const dif_pinmux_t *pinmux,
367 uint8_t i2c_id) {
368 // Configure sda pin.
369 TRY(dif_pinmux_input_select(pinmux, kI2cPinmuxPins[i2c_id].sda.peripheral_in,
371
372 // Configure scl pin.
373 TRY(dif_pinmux_input_select(pinmux, kI2cPinmuxPins[i2c_id].scl.peripheral_in,
375 return OK_STATUS();
376}
377
378status_t i2c_testutils_set_speed(const dif_i2c_t *i2c, dif_i2c_speed_t speed) {
379 uint32_t speed_khz = 0;
380 switch (speed) {
382 LOG_INFO("Setting i2c to %s mode.", "Standard (100kHz)");
383 speed_khz = 100;
384 break;
385 case kDifI2cSpeedFast:
386 LOG_INFO("Setting i2c to %s mode.", "Fast (400kHz)");
387 speed_khz = 400;
388 break;
390 LOG_INFO("Setting i2c to %s mode.", "FastPlus (1000kHz)");
391 speed_khz = 1000;
392 break;
393 }
394 // I2C speed parameters.
395 dif_i2c_timing_config_t timing_config = {
396 .lowest_target_device_speed = speed,
397 .clock_period_nanos =
398 (uint32_t)udiv64_slow(1000000000, kClockFreqPeripheralHz, NULL),
399 .sda_rise_nanos = 400,
400 .sda_fall_nanos = 110,
401 .scl_period_nanos = 1000000 / speed_khz};
402
404 TRY(dif_i2c_get_status(i2c, &status));
405 if (status.enable_host) {
406 TRY(dif_i2c_host_set_enabled(i2c, kDifToggleDisabled));
407 }
408 dif_i2c_config_t config;
409 TRY(dif_i2c_compute_timing(timing_config, &config));
410
411 LOG_INFO("period:%d nanos, cycles={fall=%d, rise=%d, hi=%d, lo=%d}",
412 timing_config.clock_period_nanos, config.fall_cycles,
413 config.rise_cycles, config.scl_time_high_cycles,
414 config.scl_time_low_cycles);
415
416 TRY(dif_i2c_configure(i2c, config));
417 // Reenable if it was enabled before.
418 if (status.enable_host) {
419 TRY(dif_i2c_host_set_enabled(i2c, kDifToggleEnabled));
420 }
421
422 return OK_STATUS();
423}
424
425status_t i2c_testutils_wait_host_idle(const dif_i2c_t *i2c) {
427 do {
428 TRY(dif_i2c_get_status(i2c, &status));
429 } while (!status.host_idle);
430 return OK_STATUS();
431}
432
433status_t i2c_testutils_wait_transaction_finish(const dif_i2c_t *i2c) {
435 bool controller_halted = false;
436 do {
437 TRY(dif_i2c_irq_is_pending(i2c, kDifI2cIrqControllerHalt,
438 &controller_halted));
439 if (controller_halted) {
440 return OK_STATUS(1);
441 }
442 TRY(dif_i2c_get_status(i2c, &status));
443 } while (!status.fmt_fifo_empty);
444 return OK_STATUS(0);
445}