[dif/i2c] add autogen'd IRQ I2C DIFs

This partially addresses #8142.

Signed-off-by: Timothy Trippel <ttrippel@google.com>
diff --git a/sw/device/lib/dif/autogen/dif_i2c_autogen.c b/sw/device/lib/dif/autogen/dif_i2c_autogen.c
new file mode 100644
index 0000000..5674f91
--- /dev/null
+++ b/sw/device/lib/dif/autogen/dif_i2c_autogen.c
@@ -0,0 +1,214 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// This file is auto-generated.
+
+#include "sw/device/lib/dif/dif_i2c.h"
+
+#include "i2c_regs.h"  // Generated.
+
+/**
+ * Get the corresponding interrupt register bit offset. INTR_STATE,
+ * INTR_ENABLE and INTR_TEST registers have the same bit offsets, so this
+ * routine can be reused.
+ */
+static bool i2c_get_irq_bit_index(dif_i2c_irq_t irq,
+                                  bitfield_bit32_index_t *index_out) {
+  switch (irq) {
+    case kDifI2cIrqFmtWatermark:
+      *index_out = I2C_INTR_STATE_FMT_WATERMARK_BIT;
+      break;
+    case kDifI2cIrqRxWatermark:
+      *index_out = I2C_INTR_STATE_RX_WATERMARK_BIT;
+      break;
+    case kDifI2cIrqFmtOverflow:
+      *index_out = I2C_INTR_STATE_FMT_OVERFLOW_BIT;
+      break;
+    case kDifI2cIrqRxOverflow:
+      *index_out = I2C_INTR_STATE_RX_OVERFLOW_BIT;
+      break;
+    case kDifI2cIrqNak:
+      *index_out = I2C_INTR_STATE_NAK_BIT;
+      break;
+    case kDifI2cIrqSclInterference:
+      *index_out = I2C_INTR_STATE_SCL_INTERFERENCE_BIT;
+      break;
+    case kDifI2cIrqSdaInterference:
+      *index_out = I2C_INTR_STATE_SDA_INTERFERENCE_BIT;
+      break;
+    case kDifI2cIrqStretchTimeout:
+      *index_out = I2C_INTR_STATE_STRETCH_TIMEOUT_BIT;
+      break;
+    case kDifI2cIrqSdaUnstable:
+      *index_out = I2C_INTR_STATE_SDA_UNSTABLE_BIT;
+      break;
+    case kDifI2cIrqTransComplete:
+      *index_out = I2C_INTR_STATE_TRANS_COMPLETE_BIT;
+      break;
+    case kDifI2cIrqTxEmpty:
+      *index_out = I2C_INTR_STATE_TX_EMPTY_BIT;
+      break;
+    case kDifI2cIrqTxNonempty:
+      *index_out = I2C_INTR_STATE_TX_NONEMPTY_BIT;
+      break;
+    case kDifI2cIrqTxOverflow:
+      *index_out = I2C_INTR_STATE_TX_OVERFLOW_BIT;
+      break;
+    case kDifI2cIrqAcqOverflow:
+      *index_out = I2C_INTR_STATE_ACQ_OVERFLOW_BIT;
+      break;
+    case kDifI2cIrqAckStop:
+      *index_out = I2C_INTR_STATE_ACK_STOP_BIT;
+      break;
+    case kDifI2cIrqHostTimeout:
+      *index_out = I2C_INTR_STATE_HOST_TIMEOUT_BIT;
+      break;
+    default:
+      return false;
+  }
+
+  return true;
+}
+
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_get_state(const dif_i2c_t *i2c,
+                                   dif_i2c_irq_state_snapshot_t *snapshot) {
+  if (i2c == NULL || snapshot == NULL) {
+    return kDifBadArg;
+  }
+
+  *snapshot = mmio_region_read32(i2c->base_addr, I2C_INTR_STATE_REG_OFFSET);
+
+  return kDifOk;
+}
+
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_is_pending(const dif_i2c_t *i2c, dif_i2c_irq_t irq,
+                                    bool *is_pending) {
+  if (i2c == NULL || is_pending == NULL) {
+    return kDifBadArg;
+  }
+
+  bitfield_bit32_index_t index;
+  if (!i2c_get_irq_bit_index(irq, &index)) {
+    return kDifBadArg;
+  }
+
+  uint32_t intr_state_reg =
+      mmio_region_read32(i2c->base_addr, I2C_INTR_STATE_REG_OFFSET);
+
+  *is_pending = bitfield_bit32_read(intr_state_reg, index);
+
+  return kDifOk;
+}
+
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_acknowledge(const dif_i2c_t *i2c, dif_i2c_irq_t irq) {
+  if (i2c == NULL) {
+    return kDifBadArg;
+  }
+
+  bitfield_bit32_index_t index;
+  if (!i2c_get_irq_bit_index(irq, &index)) {
+    return kDifBadArg;
+  }
+
+  // Writing to the register clears the corresponding bits (Write-one clear).
+  uint32_t intr_state_reg = bitfield_bit32_write(0, index, true);
+  mmio_region_write32(i2c->base_addr, I2C_INTR_STATE_REG_OFFSET,
+                      intr_state_reg);
+
+  return kDifOk;
+}
+
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_get_enabled(const dif_i2c_t *i2c, dif_i2c_irq_t irq,
+                                     dif_toggle_t *state) {
+  if (i2c == NULL || state == NULL) {
+    return kDifBadArg;
+  }
+
+  bitfield_bit32_index_t index;
+  if (!i2c_get_irq_bit_index(irq, &index)) {
+    return kDifBadArg;
+  }
+
+  uint32_t intr_enable_reg =
+      mmio_region_read32(i2c->base_addr, I2C_INTR_ENABLE_REG_OFFSET);
+
+  bool is_enabled = bitfield_bit32_read(intr_enable_reg, index);
+  *state = is_enabled ? kDifToggleEnabled : kDifToggleDisabled;
+
+  return kDifOk;
+}
+
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_set_enabled(const dif_i2c_t *i2c, dif_i2c_irq_t irq,
+                                     dif_toggle_t state) {
+  if (i2c == NULL) {
+    return kDifBadArg;
+  }
+
+  bitfield_bit32_index_t index;
+  if (!i2c_get_irq_bit_index(irq, &index)) {
+    return kDifBadArg;
+  }
+
+  uint32_t intr_enable_reg =
+      mmio_region_read32(i2c->base_addr, I2C_INTR_ENABLE_REG_OFFSET);
+
+  bool enable_bit = (state == kDifToggleEnabled) ? true : false;
+  intr_enable_reg = bitfield_bit32_write(intr_enable_reg, index, enable_bit);
+  mmio_region_write32(i2c->base_addr, I2C_INTR_ENABLE_REG_OFFSET,
+                      intr_enable_reg);
+
+  return kDifOk;
+}
+
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_force(const dif_i2c_t *i2c, dif_i2c_irq_t irq) {
+  if (i2c == NULL) {
+    return kDifBadArg;
+  }
+
+  bitfield_bit32_index_t index;
+  if (!i2c_get_irq_bit_index(irq, &index)) {
+    return kDifBadArg;
+  }
+
+  uint32_t intr_test_reg = bitfield_bit32_write(0, index, true);
+  mmio_region_write32(i2c->base_addr, I2C_INTR_TEST_REG_OFFSET, intr_test_reg);
+
+  return kDifOk;
+}
+
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_disable_all(const dif_i2c_t *i2c,
+                                     dif_i2c_irq_enable_snapshot_t *snapshot) {
+  if (i2c == NULL) {
+    return kDifBadArg;
+  }
+
+  // Pass the current interrupt state to the caller, if requested.
+  if (snapshot != NULL) {
+    *snapshot = mmio_region_read32(i2c->base_addr, I2C_INTR_ENABLE_REG_OFFSET);
+  }
+
+  // Disable all interrupts.
+  mmio_region_write32(i2c->base_addr, I2C_INTR_ENABLE_REG_OFFSET, 0u);
+
+  return kDifOk;
+}
+
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_restore_all(
+    const dif_i2c_t *i2c, const dif_i2c_irq_enable_snapshot_t *snapshot) {
+  if (i2c == NULL || snapshot == NULL) {
+    return kDifBadArg;
+  }
+
+  mmio_region_write32(i2c->base_addr, I2C_INTR_ENABLE_REG_OFFSET, *snapshot);
+
+  return kDifOk;
+}
diff --git a/sw/device/lib/dif/autogen/dif_i2c_autogen.h b/sw/device/lib/dif/autogen/dif_i2c_autogen.h
new file mode 100644
index 0000000..918b76f
--- /dev/null
+++ b/sw/device/lib/dif/autogen/dif_i2c_autogen.h
@@ -0,0 +1,225 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef OPENTITAN_SW_DEVICE_LIB_DIF_AUTOGEN_DIF_I2C_AUTOGEN_H_
+#define OPENTITAN_SW_DEVICE_LIB_DIF_AUTOGEN_DIF_I2C_AUTOGEN_H_
+
+// This file is auto-generated.
+
+/**
+ * @file
+ * @brief <a href="/hw/ip/i2c/doc/">I2C</a> Device Interface Functions
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "sw/device/lib/base/macros.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/dif/dif_base.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+/**
+ * A handle to i2c.
+ *
+ * This type should be treated as opaque by users.
+ */
+typedef struct dif_i2c {
+  /**
+   * The base address for the i2c hardware registers.
+   */
+  mmio_region_t base_addr;
+} dif_i2c_t;
+
+/**
+ * A i2c interrupt request type.
+ */
+typedef enum dif_i2c_irq {
+  /**
+   * Raised when the FMT FIFO depth falls below the low watermark.
+   */
+  kDifI2cIrqFmtWatermark = 0,
+  /**
+   * Raised if the RX FIFO is past the high watermark.
+   */
+  kDifI2cIrqRxWatermark = 1,
+  /**
+   * Raised if the FMT FIFO has overflowed.
+   */
+  kDifI2cIrqFmtOverflow = 2,
+  /**
+   * Raised if the RX FIFO has overflowed.
+   */
+  kDifI2cIrqRxOverflow = 3,
+  /**
+   * Raised if there is no ACK in response to an address or data write
+   */
+  kDifI2cIrqNak = 4,
+  /**
+   * Raised if the SCL line drops early (not supported without clock
+   * synchronization).
+   */
+  kDifI2cIrqSclInterference = 5,
+  /**
+   * Raised if the SDA line goes low when host is trying to assert high
+   */
+  kDifI2cIrqSdaInterference = 6,
+  /**
+   * Raised if target stretches the clock beyond the allowed timeout period
+   */
+  kDifI2cIrqStretchTimeout = 7,
+  /**
+   * Raised if the target does not assert a constant value of SDA during
+   * transmission.
+   */
+  kDifI2cIrqSdaUnstable = 8,
+  /**
+   * Raised if the host terminates the transaction by issuing STOP or repeated
+   * START.
+   */
+  kDifI2cIrqTransComplete = 9,
+  /**
+   * Raised if the target needs data to transmit and TX FIFO is empty.
+   */
+  kDifI2cIrqTxEmpty = 10,
+  /**
+   * Raised if there are extra bytes left in TX FIFO at the end of a read.
+   */
+  kDifI2cIrqTxNonempty = 11,
+  /**
+   * Raised if TX FIFO has overflowed.
+   */
+  kDifI2cIrqTxOverflow = 12,
+  /**
+   * Raised if ACQ FIFO has overflowed.
+   */
+  kDifI2cIrqAcqOverflow = 13,
+  /**
+   * Raised if STOP is received after ACK (host sends both signals).
+   */
+  kDifI2cIrqAckStop = 14,
+  /**
+   * Raised if the host stops sending the clock during an ongoing transaction.
+   */
+  kDifI2cIrqHostTimeout = 15,
+} dif_i2c_irq_t;
+
+/**
+ * A snapshot of the state of the interrupts for this IP.
+ *
+ * This is an opaque type, to be used with the `dif_i2c_irq_get_state()`
+ * function.
+ */
+typedef uint32_t dif_i2c_irq_state_snapshot_t;
+
+/**
+ * A snapshot of the enablement state of the interrupts for this IP.
+ *
+ * This is an opaque type, to be used with the
+ * `dif_i2c_irq_disable_all()` and `dif_i2c_irq_restore_all()`
+ * functions.
+ */
+typedef uint32_t dif_i2c_irq_enable_snapshot_t;
+
+/**
+ * Returns whether a particular interrupt is currently pending.
+ *
+ * @param i2c A i2c handle.
+ * @param irq An interrupt request.
+ * @param[out] is_pending Out-param for whether the interrupt is pending.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_get_state(const dif_i2c_t *i2c,
+                                   dif_i2c_irq_state_snapshot_t *snapshot);
+
+/**
+ * Returns whether a particular interrupt is currently pending.
+ *
+ * @param i2c A i2c handle.
+ * @param irq An interrupt request.
+ * @param[out] is_pending Out-param for whether the interrupt is pending.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_is_pending(const dif_i2c_t *i2c, dif_i2c_irq_t irq,
+                                    bool *is_pending);
+
+/**
+ * Acknowledges a particular interrupt, indicating to the hardware that it has
+ * been successfully serviced.
+ *
+ * @param i2c A i2c handle.
+ * @param irq An interrupt request.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_acknowledge(const dif_i2c_t *i2c, dif_i2c_irq_t irq);
+
+/**
+ * Checks whether a particular interrupt is currently enabled or disabled.
+ *
+ * @param i2c A i2c handle.
+ * @param irq An interrupt request.
+ * @param[out] state Out-param toggle state of the interrupt.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_get_enabled(const dif_i2c_t *i2c, dif_i2c_irq_t irq,
+                                     dif_toggle_t *state);
+
+/**
+ * Sets whether a particular interrupt is currently enabled or disabled.
+ *
+ * @param i2c A i2c handle.
+ * @param irq An interrupt request.
+ * @param state The new toggle state for the interrupt.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_set_enabled(const dif_i2c_t *i2c, dif_i2c_irq_t irq,
+                                     dif_toggle_t state);
+
+/**
+ * Forces a particular interrupt, causing it to be serviced as if hardware had
+ * asserted it.
+ *
+ * @param i2c A i2c handle.
+ * @param irq An interrupt request.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_force(const dif_i2c_t *i2c, dif_i2c_irq_t irq);
+
+/**
+ * Disables all interrupts, optionally snapshotting all enable states for later
+ * restoration.
+ *
+ * @param i2c A i2c handle.
+ * @param[out] snapshot Out-param for the snapshot; may be `NULL`.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_disable_all(const dif_i2c_t *i2c,
+                                     dif_i2c_irq_enable_snapshot_t *snapshot);
+
+/**
+ * Restores interrupts from the given (enable) snapshot.
+ *
+ * @param i2c A i2c handle.
+ * @param snapshot A snapshot to restore from.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_i2c_irq_restore_all(
+    const dif_i2c_t *i2c, const dif_i2c_irq_enable_snapshot_t *snapshot);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+#endif  // OPENTITAN_SW_DEVICE_LIB_DIF_AUTOGEN_DIF_I2C_AUTOGEN_H_
diff --git a/sw/device/lib/dif/autogen/dif_i2c_autogen_unittest.cc b/sw/device/lib/dif/autogen/dif_i2c_autogen_unittest.cc
new file mode 100644
index 0000000..b5ae1a5
--- /dev/null
+++ b/sw/device/lib/dif/autogen/dif_i2c_autogen_unittest.cc
@@ -0,0 +1,285 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// This file is auto-generated.
+
+#include "sw/device/lib/dif/dif_i2c.h"
+
+#include "gtest/gtest.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/base/testing/mock_mmio.h"
+
+#include "i2c_regs.h"  // Generated.
+
+namespace dif_i2c_autogen_unittest {
+namespace {
+using ::mock_mmio::MmioTest;
+using ::mock_mmio::MockDevice;
+using ::testing::Test;
+
+class I2cTest : public Test, public MmioTest {
+ protected:
+  dif_i2c_t i2c_ = {.base_addr = dev().region()};
+};
+
+using ::testing::Eq;
+
+class IrqGetStateTest : public I2cTest {};
+
+TEST_F(IrqGetStateTest, NullArgs) {
+  dif_i2c_irq_state_snapshot_t irq_snapshot = 0;
+
+  EXPECT_EQ(dif_i2c_irq_get_state(nullptr, &irq_snapshot), kDifBadArg);
+
+  EXPECT_EQ(dif_i2c_irq_get_state(&i2c_, nullptr), kDifBadArg);
+
+  EXPECT_EQ(dif_i2c_irq_get_state(nullptr, nullptr), kDifBadArg);
+}
+
+TEST_F(IrqGetStateTest, SuccessAllRaised) {
+  dif_i2c_irq_state_snapshot_t irq_snapshot = 0;
+
+  EXPECT_READ32(I2C_INTR_STATE_REG_OFFSET,
+                std::numeric_limits<uint32_t>::max());
+  EXPECT_EQ(dif_i2c_irq_get_state(&i2c_, &irq_snapshot), kDifOk);
+  EXPECT_EQ(irq_snapshot, std::numeric_limits<uint32_t>::max());
+}
+
+TEST_F(IrqGetStateTest, SuccessNoneRaised) {
+  dif_i2c_irq_state_snapshot_t irq_snapshot = 0;
+
+  EXPECT_READ32(I2C_INTR_STATE_REG_OFFSET, 0);
+  EXPECT_EQ(dif_i2c_irq_get_state(&i2c_, &irq_snapshot), kDifOk);
+  EXPECT_EQ(irq_snapshot, 0);
+}
+
+class IrqIsPendingTest : public I2cTest {};
+
+TEST_F(IrqIsPendingTest, NullArgs) {
+  bool is_pending;
+
+  EXPECT_EQ(
+      dif_i2c_irq_is_pending(nullptr, kDifI2cIrqFmtWatermark, &is_pending),
+      kDifBadArg);
+
+  EXPECT_EQ(dif_i2c_irq_is_pending(&i2c_, kDifI2cIrqFmtWatermark, nullptr),
+            kDifBadArg);
+
+  EXPECT_EQ(dif_i2c_irq_is_pending(nullptr, kDifI2cIrqFmtWatermark, nullptr),
+            kDifBadArg);
+}
+
+TEST_F(IrqIsPendingTest, BadIrq) {
+  bool is_pending;
+  // All interrupt CSRs are 32 bit so interrupt 32 will be invalid.
+  EXPECT_EQ(dif_i2c_irq_is_pending(&i2c_, (dif_i2c_irq_t)32, &is_pending),
+            kDifBadArg);
+}
+
+TEST_F(IrqIsPendingTest, Success) {
+  bool irq_state;
+
+  // Get the first IRQ state.
+  irq_state = false;
+  EXPECT_READ32(I2C_INTR_STATE_REG_OFFSET,
+                {{I2C_INTR_STATE_FMT_WATERMARK_BIT, true}});
+  EXPECT_EQ(dif_i2c_irq_is_pending(&i2c_, kDifI2cIrqFmtWatermark, &irq_state),
+            kDifOk);
+  EXPECT_TRUE(irq_state);
+
+  // Get the last IRQ state.
+  irq_state = true;
+  EXPECT_READ32(I2C_INTR_STATE_REG_OFFSET,
+                {{I2C_INTR_STATE_HOST_TIMEOUT_BIT, false}});
+  EXPECT_EQ(dif_i2c_irq_is_pending(&i2c_, kDifI2cIrqHostTimeout, &irq_state),
+            kDifOk);
+  EXPECT_FALSE(irq_state);
+}
+
+class IrqAcknowledgeTest : public I2cTest {};
+
+TEST_F(IrqAcknowledgeTest, NullArgs) {
+  EXPECT_EQ(dif_i2c_irq_acknowledge(nullptr, kDifI2cIrqFmtWatermark),
+            kDifBadArg);
+}
+
+TEST_F(IrqAcknowledgeTest, BadIrq) {
+  EXPECT_EQ(dif_i2c_irq_acknowledge(nullptr, (dif_i2c_irq_t)32), kDifBadArg);
+}
+
+TEST_F(IrqAcknowledgeTest, Success) {
+  // Clear the first IRQ state.
+  EXPECT_WRITE32(I2C_INTR_STATE_REG_OFFSET,
+                 {{I2C_INTR_STATE_FMT_WATERMARK_BIT, true}});
+  EXPECT_EQ(dif_i2c_irq_acknowledge(&i2c_, kDifI2cIrqFmtWatermark), kDifOk);
+
+  // Clear the last IRQ state.
+  EXPECT_WRITE32(I2C_INTR_STATE_REG_OFFSET,
+                 {{I2C_INTR_STATE_HOST_TIMEOUT_BIT, true}});
+  EXPECT_EQ(dif_i2c_irq_acknowledge(&i2c_, kDifI2cIrqHostTimeout), kDifOk);
+}
+
+class IrqGetEnabledTest : public I2cTest {};
+
+TEST_F(IrqGetEnabledTest, NullArgs) {
+  dif_toggle_t irq_state;
+
+  EXPECT_EQ(
+      dif_i2c_irq_get_enabled(nullptr, kDifI2cIrqFmtWatermark, &irq_state),
+      kDifBadArg);
+
+  EXPECT_EQ(dif_i2c_irq_get_enabled(&i2c_, kDifI2cIrqFmtWatermark, nullptr),
+            kDifBadArg);
+
+  EXPECT_EQ(dif_i2c_irq_get_enabled(nullptr, kDifI2cIrqFmtWatermark, nullptr),
+            kDifBadArg);
+}
+
+TEST_F(IrqGetEnabledTest, BadIrq) {
+  dif_toggle_t irq_state;
+
+  EXPECT_EQ(dif_i2c_irq_get_enabled(&i2c_, (dif_i2c_irq_t)32, &irq_state),
+            kDifBadArg);
+}
+
+TEST_F(IrqGetEnabledTest, Success) {
+  dif_toggle_t irq_state;
+
+  // First IRQ is enabled.
+  irq_state = kDifToggleDisabled;
+  EXPECT_READ32(I2C_INTR_ENABLE_REG_OFFSET,
+                {{I2C_INTR_ENABLE_FMT_WATERMARK_BIT, true}});
+  EXPECT_EQ(dif_i2c_irq_get_enabled(&i2c_, kDifI2cIrqFmtWatermark, &irq_state),
+            kDifOk);
+  EXPECT_EQ(irq_state, kDifToggleEnabled);
+
+  // Last IRQ is disabled.
+  irq_state = kDifToggleEnabled;
+  EXPECT_READ32(I2C_INTR_ENABLE_REG_OFFSET,
+                {{I2C_INTR_ENABLE_HOST_TIMEOUT_BIT, false}});
+  EXPECT_EQ(dif_i2c_irq_get_enabled(&i2c_, kDifI2cIrqHostTimeout, &irq_state),
+            kDifOk);
+  EXPECT_EQ(irq_state, kDifToggleDisabled);
+}
+
+class IrqSetEnabledTest : public I2cTest {};
+
+TEST_F(IrqSetEnabledTest, NullArgs) {
+  dif_toggle_t irq_state = kDifToggleEnabled;
+
+  EXPECT_EQ(dif_i2c_irq_set_enabled(nullptr, kDifI2cIrqFmtWatermark, irq_state),
+            kDifBadArg);
+}
+
+TEST_F(IrqSetEnabledTest, BadIrq) {
+  dif_toggle_t irq_state = kDifToggleEnabled;
+
+  EXPECT_EQ(dif_i2c_irq_set_enabled(&i2c_, (dif_i2c_irq_t)32, irq_state),
+            kDifBadArg);
+}
+
+TEST_F(IrqSetEnabledTest, Success) {
+  dif_toggle_t irq_state;
+
+  // Enable first IRQ.
+  irq_state = kDifToggleEnabled;
+  EXPECT_MASK32(I2C_INTR_ENABLE_REG_OFFSET,
+                {{I2C_INTR_ENABLE_FMT_WATERMARK_BIT, 0x1, true}});
+  EXPECT_EQ(dif_i2c_irq_set_enabled(&i2c_, kDifI2cIrqFmtWatermark, irq_state),
+            kDifOk);
+
+  // Disable last IRQ.
+  irq_state = kDifToggleDisabled;
+  EXPECT_MASK32(I2C_INTR_ENABLE_REG_OFFSET,
+                {{I2C_INTR_ENABLE_HOST_TIMEOUT_BIT, 0x1, false}});
+  EXPECT_EQ(dif_i2c_irq_set_enabled(&i2c_, kDifI2cIrqHostTimeout, irq_state),
+            kDifOk);
+}
+
+class IrqForceTest : public I2cTest {};
+
+TEST_F(IrqForceTest, NullArgs) {
+  EXPECT_EQ(dif_i2c_irq_force(nullptr, kDifI2cIrqFmtWatermark), kDifBadArg);
+}
+
+TEST_F(IrqForceTest, BadIrq) {
+  EXPECT_EQ(dif_i2c_irq_force(nullptr, (dif_i2c_irq_t)32), kDifBadArg);
+}
+
+TEST_F(IrqForceTest, Success) {
+  // Force first IRQ.
+  EXPECT_WRITE32(I2C_INTR_TEST_REG_OFFSET,
+                 {{I2C_INTR_TEST_FMT_WATERMARK_BIT, true}});
+  EXPECT_EQ(dif_i2c_irq_force(&i2c_, kDifI2cIrqFmtWatermark), kDifOk);
+
+  // Force last IRQ.
+  EXPECT_WRITE32(I2C_INTR_TEST_REG_OFFSET,
+                 {{I2C_INTR_TEST_HOST_TIMEOUT_BIT, true}});
+  EXPECT_EQ(dif_i2c_irq_force(&i2c_, kDifI2cIrqHostTimeout), kDifOk);
+}
+
+class IrqDisableAllTest : public I2cTest {};
+
+TEST_F(IrqDisableAllTest, NullArgs) {
+  dif_i2c_irq_enable_snapshot_t irq_snapshot = 0;
+
+  EXPECT_EQ(dif_i2c_irq_disable_all(nullptr, &irq_snapshot), kDifBadArg);
+
+  EXPECT_EQ(dif_i2c_irq_disable_all(nullptr, nullptr), kDifBadArg);
+}
+
+TEST_F(IrqDisableAllTest, SuccessNoSnapshot) {
+  EXPECT_WRITE32(I2C_INTR_ENABLE_REG_OFFSET, 0);
+  EXPECT_EQ(dif_i2c_irq_disable_all(&i2c_, nullptr), kDifOk);
+}
+
+TEST_F(IrqDisableAllTest, SuccessSnapshotAllDisabled) {
+  dif_i2c_irq_enable_snapshot_t irq_snapshot = 0;
+
+  EXPECT_READ32(I2C_INTR_ENABLE_REG_OFFSET, 0);
+  EXPECT_WRITE32(I2C_INTR_ENABLE_REG_OFFSET, 0);
+  EXPECT_EQ(dif_i2c_irq_disable_all(&i2c_, &irq_snapshot), kDifOk);
+  EXPECT_EQ(irq_snapshot, 0);
+}
+
+TEST_F(IrqDisableAllTest, SuccessSnapshotAllEnabled) {
+  dif_i2c_irq_enable_snapshot_t irq_snapshot = 0;
+
+  EXPECT_READ32(I2C_INTR_ENABLE_REG_OFFSET,
+                std::numeric_limits<uint32_t>::max());
+  EXPECT_WRITE32(I2C_INTR_ENABLE_REG_OFFSET, 0);
+  EXPECT_EQ(dif_i2c_irq_disable_all(&i2c_, &irq_snapshot), kDifOk);
+  EXPECT_EQ(irq_snapshot, std::numeric_limits<uint32_t>::max());
+}
+
+class IrqRestoreAllTest : public I2cTest {};
+
+TEST_F(IrqRestoreAllTest, NullArgs) {
+  dif_i2c_irq_enable_snapshot_t irq_snapshot = 0;
+
+  EXPECT_EQ(dif_i2c_irq_restore_all(nullptr, &irq_snapshot), kDifBadArg);
+
+  EXPECT_EQ(dif_i2c_irq_restore_all(&i2c_, nullptr), kDifBadArg);
+
+  EXPECT_EQ(dif_i2c_irq_restore_all(nullptr, nullptr), kDifBadArg);
+}
+
+TEST_F(IrqRestoreAllTest, SuccessAllEnabled) {
+  dif_i2c_irq_enable_snapshot_t irq_snapshot =
+      std::numeric_limits<uint32_t>::max();
+
+  EXPECT_WRITE32(I2C_INTR_ENABLE_REG_OFFSET,
+                 std::numeric_limits<uint32_t>::max());
+  EXPECT_EQ(dif_i2c_irq_restore_all(&i2c_, &irq_snapshot), kDifOk);
+}
+
+TEST_F(IrqRestoreAllTest, SuccessAllDisabled) {
+  dif_i2c_irq_enable_snapshot_t irq_snapshot = 0;
+
+  EXPECT_WRITE32(I2C_INTR_ENABLE_REG_OFFSET, 0);
+  EXPECT_EQ(dif_i2c_irq_restore_all(&i2c_, &irq_snapshot), kDifOk);
+}
+
+}  // namespace
+}  // namespace dif_i2c_autogen_unittest