blob: 9599af347a4a65c706ba4d10940ac415ceaa1921 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/dif/dif_i2c.h"
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/macros.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_base.h"
#include "i2c_regs.h" // Generated
/**
* Performs a 32-bit integer unsigned division, rounding up. The bottom
* 16 bits of the result are then returned.
*
* As usual, a divisor of 0 is still Undefined Behavior.
*/
static uint16_t round_up_divide(uint32_t a, uint32_t b) {
return ((a - 1) / b) + 1;
}
/**
* Reads i2c status bits from registers
*/
dif_result_t dif_i2c_get_status(const dif_i2c_t *i2c,
dif_i2c_status_t *status) {
if (i2c == NULL || status == NULL) {
return kDifBadArg;
}
uint32_t reg = mmio_region_read32(i2c->base_addr, I2C_CTRL_REG_OFFSET);
status->enable_host = bitfield_bit32_read(reg, I2C_CTRL_ENABLEHOST_BIT);
status->enable_target = bitfield_bit32_read(reg, I2C_CTRL_ENABLETARGET_BIT);
status->line_loopback = bitfield_bit32_read(reg, I2C_CTRL_LLPBK_BIT);
reg = mmio_region_read32(i2c->base_addr, I2C_STATUS_REG_OFFSET);
status->fmt_fifo_full = bitfield_bit32_read(reg, I2C_STATUS_FMTFULL_BIT);
status->rx_fifo_full = bitfield_bit32_read(reg, I2C_STATUS_RXFULL_BIT);
status->fmt_fifo_empty = bitfield_bit32_read(reg, I2C_STATUS_FMTEMPTY_BIT);
status->rx_fifo_empty = bitfield_bit32_read(reg, I2C_STATUS_RXEMPTY_BIT);
status->host_idle = bitfield_bit32_read(reg, I2C_STATUS_HOSTIDLE_BIT);
status->target_idle = bitfield_bit32_read(reg, I2C_STATUS_TARGETIDLE_BIT);
status->tx_fifo_full = bitfield_bit32_read(reg, I2C_STATUS_TXFULL_BIT);
status->acq_fifo_full = bitfield_bit32_read(reg, I2C_STATUS_ACQFULL_BIT);
status->tx_fifo_empty = bitfield_bit32_read(reg, I2C_STATUS_TXEMPTY_BIT);
status->acq_fifo_empty = bitfield_bit32_read(reg, I2C_STATUS_ACQEMPTY_BIT);
return kDifOk;
}
/**
* Computes default timing parameters for a particular I2C speed, given the
* clock period, in nanoseconds.
*
* Returns an unspecified value for an invalid speed.
*/
static dif_i2c_config_t default_timing_for_speed(dif_i2c_speed_t speed,
uint32_t clock_period_nanos) {
// NOTE: All constants below are lifted from Table 10 of the I2C spec.
// All literal values are given in nanoseconds; we don't bother putting
// these into constants since they are not used anywhere else.
switch (speed) {
case kDifI2cSpeedStandard:
return (dif_i2c_config_t){
.scl_time_high_cycles = round_up_divide(4000, clock_period_nanos),
.scl_time_low_cycles = round_up_divide(4700, clock_period_nanos),
.start_signal_setup_cycles =
round_up_divide(4700, clock_period_nanos),
.start_signal_hold_cycles = round_up_divide(4000, clock_period_nanos),
.data_signal_setup_cycles = round_up_divide(250, clock_period_nanos),
.data_signal_hold_cycles = 1,
.stop_signal_setup_cycles = round_up_divide(4000, clock_period_nanos),
.stop_signal_hold_cycles = round_up_divide(4700, clock_period_nanos),
};
case kDifI2cSpeedFast:
return (dif_i2c_config_t){
.scl_time_high_cycles = round_up_divide(600, clock_period_nanos),
.scl_time_low_cycles = round_up_divide(1300, clock_period_nanos),
.start_signal_setup_cycles = round_up_divide(600, clock_period_nanos),
.start_signal_hold_cycles = round_up_divide(600, clock_period_nanos),
.data_signal_setup_cycles = round_up_divide(100, clock_period_nanos),
.data_signal_hold_cycles = 1,
.stop_signal_setup_cycles = round_up_divide(600, clock_period_nanos),
.stop_signal_hold_cycles = round_up_divide(1300, clock_period_nanos),
};
case kDifI2cSpeedFastPlus:
return (dif_i2c_config_t){
.scl_time_high_cycles = round_up_divide(260, clock_period_nanos),
.scl_time_low_cycles = round_up_divide(500, clock_period_nanos),
.start_signal_setup_cycles = round_up_divide(260, clock_period_nanos),
.start_signal_hold_cycles = round_up_divide(260, clock_period_nanos),
.data_signal_setup_cycles = round_up_divide(50, clock_period_nanos),
.data_signal_hold_cycles = 1,
.stop_signal_setup_cycles = round_up_divide(260, clock_period_nanos),
.stop_signal_hold_cycles = round_up_divide(500, clock_period_nanos),
};
default:
return (dif_i2c_config_t){0};
}
}
static const uint32_t kNanosPerKBaud = 1000000; // One million.
dif_result_t dif_i2c_compute_timing(dif_i2c_timing_config_t timing_config,
dif_i2c_config_t *config) {
if (config == NULL) {
return kDifBadArg;
}
uint32_t lowest_target_device_speed_khz;
switch (timing_config.lowest_target_device_speed) {
case kDifI2cSpeedStandard:
lowest_target_device_speed_khz = 100;
break;
case kDifI2cSpeedFast:
lowest_target_device_speed_khz = 400;
break;
case kDifI2cSpeedFastPlus:
lowest_target_device_speed_khz = 1000;
break;
default:
return kDifBadArg;
}
// This code follows the algorithm given in
// https://docs.opentitan.org/hw/ip/i2c/doc/index.html#initialization
*config = default_timing_for_speed(timing_config.lowest_target_device_speed,
timing_config.clock_period_nanos);
config->rise_cycles = round_up_divide(timing_config.sda_rise_nanos,
timing_config.clock_period_nanos);
config->fall_cycles = round_up_divide(timing_config.sda_fall_nanos,
timing_config.clock_period_nanos);
uint32_t scl_period_nanos = timing_config.scl_period_nanos;
uint32_t slowest_scl_period_nanos =
kNanosPerKBaud / lowest_target_device_speed_khz;
if (scl_period_nanos < slowest_scl_period_nanos) {
scl_period_nanos = slowest_scl_period_nanos;
}
uint16_t scl_period_cycles =
round_up_divide(scl_period_nanos, timing_config.clock_period_nanos);
// Lengthen the SCL high period to accommodate the desired SCL period.
uint16_t lengthened_high_cycles = scl_period_cycles -
config->scl_time_low_cycles -
config->rise_cycles - config->fall_cycles;
if (lengthened_high_cycles > config->scl_time_high_cycles) {
config->scl_time_high_cycles = lengthened_high_cycles;
}
return kDifOk;
}
dif_result_t dif_i2c_configure(const dif_i2c_t *i2c, dif_i2c_config_t config) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t timing0 = 0;
timing0 = bitfield_field32_write(timing0, I2C_TIMING0_THIGH_FIELD,
config.scl_time_high_cycles);
timing0 = bitfield_field32_write(timing0, I2C_TIMING0_TLOW_FIELD,
config.scl_time_low_cycles);
mmio_region_write32(i2c->base_addr, I2C_TIMING0_REG_OFFSET, timing0);
uint32_t timing1 = 0;
timing1 = bitfield_field32_write(timing1, I2C_TIMING1_T_R_FIELD,
config.rise_cycles);
timing1 = bitfield_field32_write(timing1, I2C_TIMING1_T_F_FIELD,
config.fall_cycles);
mmio_region_write32(i2c->base_addr, I2C_TIMING1_REG_OFFSET, timing1);
uint32_t timing2 = 0;
timing2 = bitfield_field32_write(timing2, I2C_TIMING2_TSU_STA_FIELD,
config.start_signal_setup_cycles);
timing2 = bitfield_field32_write(timing2, I2C_TIMING2_THD_STA_FIELD,
config.start_signal_hold_cycles);
mmio_region_write32(i2c->base_addr, I2C_TIMING2_REG_OFFSET, timing2);
uint32_t timing3 = 0;
timing3 = bitfield_field32_write(timing3, I2C_TIMING3_TSU_DAT_FIELD,
config.data_signal_setup_cycles);
timing3 = bitfield_field32_write(timing3, I2C_TIMING3_THD_DAT_FIELD,
config.data_signal_hold_cycles);
mmio_region_write32(i2c->base_addr, I2C_TIMING3_REG_OFFSET, timing3);
uint32_t timing4 = 0;
timing4 = bitfield_field32_write(timing4, I2C_TIMING4_TSU_STO_FIELD,
config.stop_signal_setup_cycles);
timing4 = bitfield_field32_write(timing4, I2C_TIMING4_T_BUF_FIELD,
config.stop_signal_hold_cycles);
mmio_region_write32(i2c->base_addr, I2C_TIMING4_REG_OFFSET, timing4);
return kDifOk;
}
dif_result_t dif_i2c_reset_rx_fifo(const dif_i2c_t *i2c) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t reg = mmio_region_read32(i2c->base_addr, I2C_FIFO_CTRL_REG_OFFSET);
reg = bitfield_bit32_write(reg, I2C_FIFO_CTRL_RXRST_BIT, true);
mmio_region_write32(i2c->base_addr, I2C_FIFO_CTRL_REG_OFFSET, reg);
return kDifOk;
}
dif_result_t dif_i2c_reset_fmt_fifo(const dif_i2c_t *i2c) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t reg = mmio_region_read32(i2c->base_addr, I2C_FIFO_CTRL_REG_OFFSET);
reg = bitfield_bit32_write(reg, I2C_FIFO_CTRL_FMTRST_BIT, true);
mmio_region_write32(i2c->base_addr, I2C_FIFO_CTRL_REG_OFFSET, reg);
return kDifOk;
}
dif_result_t dif_i2c_reset_tx_fifo(const dif_i2c_t *i2c) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t reg = mmio_region_read32(i2c->base_addr, I2C_FIFO_CTRL_REG_OFFSET);
reg = bitfield_bit32_write(reg, I2C_FIFO_CTRL_TXRST_BIT, true);
mmio_region_write32(i2c->base_addr, I2C_FIFO_CTRL_REG_OFFSET, reg);
return kDifOk;
}
dif_result_t dif_i2c_reset_acq_fifo(const dif_i2c_t *i2c) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t reg = mmio_region_read32(i2c->base_addr, I2C_FIFO_CTRL_REG_OFFSET);
reg = bitfield_bit32_write(reg, I2C_FIFO_CTRL_ACQRST_BIT, true);
mmio_region_write32(i2c->base_addr, I2C_FIFO_CTRL_REG_OFFSET, reg);
return kDifOk;
}
dif_result_t dif_i2c_set_watermarks(const dif_i2c_t *i2c,
dif_i2c_level_t rx_level,
dif_i2c_level_t fmt_level) {
if (i2c == NULL) {
return kDifBadArg;
}
ptrdiff_t rx_level_value;
switch (rx_level) {
case kDifI2cLevel1Byte:
rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL1;
break;
case kDifI2cLevel4Byte:
rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL4;
break;
case kDifI2cLevel8Byte:
rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL8;
break;
case kDifI2cLevel16Byte:
rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL16;
break;
case kDifI2cLevel30Byte:
rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL30;
break;
default:
return kDifBadArg;
}
ptrdiff_t fmt_level_value;
switch (fmt_level) {
case kDifI2cLevel1Byte:
fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL1;
break;
case kDifI2cLevel4Byte:
fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL4;
break;
case kDifI2cLevel8Byte:
fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL8;
break;
case kDifI2cLevel16Byte:
fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL16;
break;
default:
return kDifBadArg;
}
uint32_t ctrl_value =
mmio_region_read32(i2c->base_addr, I2C_FIFO_CTRL_REG_OFFSET);
ctrl_value = bitfield_field32_write(ctrl_value, I2C_FIFO_CTRL_RXILVL_FIELD,
rx_level_value);
ctrl_value = bitfield_field32_write(ctrl_value, I2C_FIFO_CTRL_FMTILVL_FIELD,
fmt_level_value);
mmio_region_write32(i2c->base_addr, I2C_FIFO_CTRL_REG_OFFSET, ctrl_value);
return kDifOk;
}
dif_result_t dif_i2c_host_set_enabled(const dif_i2c_t *i2c,
dif_toggle_t state) {
if (i2c == NULL) {
return kDifBadArg;
}
if (!dif_is_valid_toggle(state)) {
return kDifBadArg;
}
bool flag = dif_toggle_to_bool(state);
uint32_t reg = mmio_region_read32(i2c->base_addr, I2C_CTRL_REG_OFFSET);
reg = bitfield_bit32_write(reg, I2C_CTRL_ENABLEHOST_BIT, flag);
mmio_region_write32(i2c->base_addr, I2C_CTRL_REG_OFFSET, reg);
return kDifOk;
}
dif_result_t dif_i2c_device_set_enabled(const dif_i2c_t *i2c,
dif_toggle_t state) {
if (i2c == NULL) {
return kDifBadArg;
}
if (!dif_is_valid_toggle(state)) {
return kDifBadArg;
}
bool flag = dif_toggle_to_bool(state);
uint32_t reg = mmio_region_read32(i2c->base_addr, I2C_CTRL_REG_OFFSET);
reg = bitfield_bit32_write(reg, I2C_CTRL_ENABLETARGET_BIT, flag);
mmio_region_write32(i2c->base_addr, I2C_CTRL_REG_OFFSET, reg);
return kDifOk;
}
dif_result_t dif_i2c_line_loopback_set_enabled(const dif_i2c_t *i2c,
dif_toggle_t state) {
if (i2c == NULL) {
return kDifBadArg;
}
if (!dif_is_valid_toggle(state)) {
return kDifBadArg;
}
bool flag = dif_toggle_to_bool(state);
uint32_t reg = mmio_region_read32(i2c->base_addr, I2C_CTRL_REG_OFFSET);
reg = bitfield_bit32_write(reg, I2C_CTRL_LLPBK_BIT, flag);
mmio_region_write32(i2c->base_addr, I2C_CTRL_REG_OFFSET, reg);
return kDifOk;
}
dif_result_t dif_i2c_override_set_enabled(const dif_i2c_t *i2c,
dif_toggle_t state) {
if (i2c == NULL) {
return kDifBadArg;
}
if (!dif_is_valid_toggle(state)) {
return kDifBadArg;
}
bool flag = dif_toggle_to_bool(state);
uint32_t reg = mmio_region_read32(i2c->base_addr, I2C_OVRD_REG_OFFSET);
reg = bitfield_bit32_write(reg, I2C_OVRD_TXOVRDEN_BIT, flag);
mmio_region_write32(i2c->base_addr, I2C_OVRD_REG_OFFSET, reg);
return kDifOk;
}
dif_result_t dif_i2c_override_drive_pins(const dif_i2c_t *i2c, bool scl,
bool sda) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t override_val =
mmio_region_read32(i2c->base_addr, I2C_OVRD_REG_OFFSET);
override_val = bitfield_bit32_write(override_val, I2C_OVRD_SCLVAL_BIT, scl);
override_val = bitfield_bit32_write(override_val, I2C_OVRD_SDAVAL_BIT, sda);
mmio_region_write32(i2c->base_addr, I2C_OVRD_REG_OFFSET, override_val);
return kDifOk;
}
dif_result_t dif_i2c_override_sample_pins(const dif_i2c_t *i2c,
uint16_t *scl_samples,
uint16_t *sda_samples) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t samples = mmio_region_read32(i2c->base_addr, I2C_VAL_REG_OFFSET);
if (scl_samples != NULL) {
*scl_samples = bitfield_field32_read(samples, I2C_VAL_SCL_RX_FIELD);
}
if (sda_samples != NULL) {
*sda_samples = bitfield_field32_read(samples, I2C_VAL_SDA_RX_FIELD);
}
return kDifOk;
}
dif_result_t dif_i2c_get_fifo_levels(const dif_i2c_t *i2c,
uint8_t *fmt_fifo_level,
uint8_t *rx_fifo_level,
uint8_t *tx_fifo_level,
uint8_t *acq_fifo_level) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t values =
mmio_region_read32(i2c->base_addr, I2C_FIFO_STATUS_REG_OFFSET);
if (fmt_fifo_level != NULL) {
*fmt_fifo_level =
bitfield_field32_read(values, I2C_FIFO_STATUS_FMTLVL_FIELD);
}
if (rx_fifo_level != NULL) {
*rx_fifo_level = bitfield_field32_read(values, I2C_FIFO_STATUS_RXLVL_FIELD);
}
if (tx_fifo_level != NULL) {
*tx_fifo_level = bitfield_field32_read(values, I2C_FIFO_STATUS_TXLVL_FIELD);
}
if (acq_fifo_level != NULL) {
*acq_fifo_level =
bitfield_field32_read(values, I2C_FIFO_STATUS_ACQLVL_FIELD);
}
return kDifOk;
}
dif_result_t dif_i2c_read_byte(const dif_i2c_t *i2c, uint8_t *byte) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t values = mmio_region_read32(i2c->base_addr, I2C_RDATA_REG_OFFSET);
if (byte != NULL) {
*byte = bitfield_field32_read(values, I2C_RDATA_RDATA_FIELD);
}
return kDifOk;
}
dif_result_t dif_i2c_write_byte_raw(const dif_i2c_t *i2c, uint8_t byte,
dif_i2c_fmt_flags_t flags) {
if (i2c == NULL) {
return kDifBadArg;
}
// Validate that "write only" flags and "read only" flags are not set
// simultaneously.
bool has_write_flags = flags.start || flags.suppress_nak_irq;
bool has_read_flags = flags.read || flags.read_cont;
if (has_write_flags && has_read_flags) {
return kDifBadArg;
}
// Also, read_cont requires read.
if (flags.read_cont && !flags.read) {
return kDifBadArg;
}
uint32_t fmt_byte = 0;
fmt_byte = bitfield_field32_write(fmt_byte, I2C_FDATA_FBYTE_FIELD, byte);
fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_START_BIT, flags.start);
fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_STOP_BIT, flags.stop);
fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_READ_BIT, flags.read);
fmt_byte =
bitfield_bit32_write(fmt_byte, I2C_FDATA_RCONT_BIT, flags.read_cont);
fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_NAKOK_BIT,
flags.suppress_nak_irq);
mmio_region_write32(i2c->base_addr, I2C_FDATA_REG_OFFSET, fmt_byte);
return kDifOk;
}
dif_result_t dif_i2c_write_byte(const dif_i2c_t *i2c, uint8_t byte,
dif_i2c_fmt_t code, bool suppress_nak_irq) {
if (i2c == NULL) {
return kDifBadArg;
}
// Validate that `suppress_nak_irq` has not been mixed with an Rx code.
if (suppress_nak_irq) {
switch (code) {
case kDifI2cFmtRx:
case kDifI2cFmtRxContinue:
case kDifI2cFmtRxStop:
return kDifBadArg;
default:
break;
}
}
// Convert the format code into flags.
dif_i2c_fmt_flags_t flags = {.suppress_nak_irq = suppress_nak_irq};
switch (code) {
case kDifI2cFmtStart:
flags.start = true;
break;
case kDifI2cFmtTx:
break;
case kDifI2cFmtTxStop:
flags.stop = true;
break;
case kDifI2cFmtRx:
flags.read = true;
break;
case kDifI2cFmtRxContinue:
flags.read = true;
flags.read_cont = true;
break;
case kDifI2cFmtRxStop:
flags.read = true;
flags.stop = true;
break;
default:
return kDifBadArg;
}
return dif_i2c_write_byte_raw(i2c, byte, flags);
}
dif_result_t dif_i2c_transmit_byte(const dif_i2c_t *i2c, uint8_t byte) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t tx_byte = 0;
tx_byte = bitfield_field32_write(tx_byte, I2C_TXDATA_TXDATA_FIELD, byte);
mmio_region_write32(i2c->base_addr, I2C_TXDATA_REG_OFFSET, tx_byte);
return kDifOk;
}
dif_result_t dif_i2c_acquire_byte(const dif_i2c_t *i2c, uint8_t *byte,
dif_i2c_signal_t *signal) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t acq_byte =
mmio_region_read32(i2c->base_addr, I2C_ACQDATA_REG_OFFSET);
if (byte != NULL) {
*byte = bitfield_field32_read(acq_byte, I2C_ACQDATA_ABYTE_FIELD);
}
if (signal != NULL) {
*signal = bitfield_field32_read(acq_byte, I2C_ACQDATA_SIGNAL_FIELD);
}
return kDifOk;
}
dif_result_t dif_i2c_enable_clock_stretching_timeout(const dif_i2c_t *i2c,
dif_toggle_t enable,
uint32_t cycles) {
if (i2c == NULL) {
return kDifBadArg;
}
if (!dif_is_valid_toggle(enable)) {
return kDifBadArg;
}
bool flag = dif_toggle_to_bool(enable);
uint32_t config = 0;
config = bitfield_bit32_write(config, I2C_TIMEOUT_CTRL_EN_BIT, flag);
config = bitfield_field32_write(config, I2C_TIMEOUT_CTRL_VAL_FIELD, cycles);
mmio_region_write32(i2c->base_addr, I2C_TIMEOUT_CTRL_REG_OFFSET, config);
return kDifOk;
}
dif_result_t dif_i2c_set_device_id(const dif_i2c_t *i2c, dif_i2c_id_t *id0,
dif_i2c_id_t *id1) {
if (i2c == NULL) {
return kDifBadArg;
}
uint32_t config = 0;
if (id0 != NULL) {
config = bitfield_field32_write(config, I2C_TARGET_ID_ADDRESS0_FIELD,
id0->address);
config =
bitfield_field32_write(config, I2C_TARGET_ID_MASK0_FIELD, id0->mask);
} else {
// Don't listen by default
config = bitfield_field32_write(config, I2C_TARGET_ID_ADDRESS0_FIELD, 0x7f);
}
if (id1 != NULL) {
config = bitfield_field32_write(config, I2C_TARGET_ID_ADDRESS1_FIELD,
id1->address);
config =
bitfield_field32_write(config, I2C_TARGET_ID_MASK1_FIELD, id1->mask);
} else {
// Don't listen by default
config = bitfield_field32_write(config, I2C_TARGET_ID_ADDRESS1_FIELD, 0x7f);
}
mmio_region_write32(i2c->base_addr, I2C_TARGET_ID_REG_OFFSET, config);
return kDifOk;
}
dif_result_t dif_i2c_set_host_timeout(const dif_i2c_t *i2c, uint32_t cycles) {
if (i2c == NULL) {
return kDifBadArg;
}
mmio_region_write32(i2c->base_addr, I2C_HOST_TIMEOUT_CTRL_REG_OFFSET, cycles);
return kDifOk;
}