[sw/dif_pwrmgr] Implement Power Manager DIF library

Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/lib/dif/dif_pwrmgr.c b/sw/device/lib/dif/dif_pwrmgr.c
index e24a5c4..81d932a 100644
--- a/sw/device/lib/dif/dif_pwrmgr.c
+++ b/sw/device/lib/dif/dif_pwrmgr.c
@@ -4,7 +4,573 @@
 
 #include "sw/device/lib/dif/dif_pwrmgr.h"
 
+#include "sw/device/lib/base/bitfield.h"
+#include "sw/device/lib/base/mmio.h"
 
 #include "pwrmgr_regs.h"  // Generated.
 
-#error "Power Manager DIF library is not implemented yet."
+/**
+ * Following static assertions make sure that generated values match the
+ * definitions in the header, which we rely on for a simpler implementation.
+ * These constants and their usages must be revisited if there is a change in
+ * hardware.
+ */
+
+/**
+ * Relevant bits of the control register must start at
+ * `PWRMGR_CONTROL_CORE_CLK_EN` and be in the same order as
+ * `dif_pwrmgr_domain_option_t` constants.
+ */
+_Static_assert(kDifPwrmgrDomainOptionCoreClockInLowPower ==
+                   (1u << (PWRMGR_CONTROL_CORE_CLK_EN -
+                           PWRMGR_CONTROL_CORE_CLK_EN)),
+               "Layout of control register changed.");
+
+_Static_assert(kDifPwrmgrDomainOptionIoClockInLowPower ==
+                   (1u << (PWRMGR_CONTROL_IO_CLK_EN -
+                           PWRMGR_CONTROL_CORE_CLK_EN)),
+               "Layout of control register changed.");
+
+_Static_assert(kDifPwrmgrDomainOptionUsbClockInLowPower ==
+                   (1u << (PWRMGR_CONTROL_USB_CLK_EN_LP -
+                           PWRMGR_CONTROL_CORE_CLK_EN)),
+               "Layout of control register changed.");
+
+_Static_assert(kDifPwrmgrDomainOptionUsbClockInActivePower ==
+                   (1u << (PWRMGR_CONTROL_USB_CLK_EN_ACTIVE -
+                           PWRMGR_CONTROL_CORE_CLK_EN)),
+               "Layout of control register changed.");
+
+_Static_assert(kDifPwrmgrDomainOptionMainPowerInLowPower ==
+                   (1u << (PWRMGR_CONTROL_MAIN_PD_N -
+                           PWRMGR_CONTROL_CORE_CLK_EN)),
+               "Layout of control register changed.");
+
+/**
+ * Bitfield for interpreting the configuration options in the control
+ * register.
+ */
+static const bitfield_field32_t kDomainConfigBitfield = {
+    .mask = kDifPwrmgrDomainOptionCoreClockInLowPower |
+            kDifPwrmgrDomainOptionIoClockInLowPower |
+            kDifPwrmgrDomainOptionUsbClockInLowPower |
+            kDifPwrmgrDomainOptionUsbClockInActivePower |
+            kDifPwrmgrDomainOptionMainPowerInLowPower,
+    .index = PWRMGR_CONTROL_CORE_CLK_EN,
+};
+
+/**
+ * Relevant bits of the WAKEUP_EN and WAKE_INFO registers must start at `0` and
+ * be in the same order as `dif_pwrmgr_wakeup_request_source_t` constants.
+ */
+_Static_assert(kDifPwrmgrWakeupRequestSourceOne ==
+                   (1u << PWRMGR_WAKEUP_EN_EN_0),
+               "Layout of WAKEUP_EN register changed.");
+_Static_assert(kDifPwrmgrWakeupRequestSourceOne ==
+                   (1u << PWRMGR_WAKE_INFO_REASONS),
+               "Layout of WAKE_INFO register changed.");
+
+/**
+ * Relevant bits of the RESET_EN register must start at `0` and be in the same
+ * order as `dif_pwrmgr_reset_request_source_t` constants.
+ */
+_Static_assert(kDifPwrmgrResetRequestSourceOne == (1u << PWRMGR_RESET_EN_EN_0),
+               "Layout of RESET_EN register changed.");
+
+/**
+ * `dif_pwrmgr_irq_t` constants must match the corresponding generated values.
+ */
+_Static_assert(kDifPwrmgrIrqWakeup == PWRMGR_INTR_COMMON_WAKEUP,
+               "Layout of interrupt registers changed.");
+
+/**
+ * Register information for a request type.
+ */
+typedef struct request_reg_info {
+  ptrdiff_t write_enable_reg_offset;
+  bitfield_bit32_index_t write_enable_bit_index;
+  ptrdiff_t sources_enable_reg_offset;
+  ptrdiff_t cur_req_sources_reg_offset;
+  bitfield_field32_t bitfield;
+} request_reg_info_t;
+
+/**
+ * Register information for wakeup and reset requests.
+ *
+ * Wakeup and reset requests follow the same logic for configuration and
+ * monitoring but use different registers. Defining these constants here
+ * allows us to use the same code for both types of requests.
+ */
+static const request_reg_info_t request_reg_infos[2] = {
+    [kDifPwrmgrReqTypeWakeup] =
+        {
+            .write_enable_reg_offset = PWRMGR_WAKEUP_EN_REGWEN_REG_OFFSET,
+            .write_enable_bit_index = PWRMGR_WAKEUP_EN_REGWEN_EN,
+            .sources_enable_reg_offset = PWRMGR_WAKEUP_EN_REG_OFFSET,
+            .cur_req_sources_reg_offset = PWRMGR_WAKE_STATUS_REG_OFFSET,
+            .bitfield =
+                {
+                    .mask = kDifPwrmgrWakeupRequestSourceOne,
+                    .index = 0,
+                },
+        },
+    [kDifPwrmgrReqTypeReset] =
+        {
+            .write_enable_reg_offset = PWRMGR_RESET_EN_REGWEN_REG_OFFSET,
+            .write_enable_bit_index = PWRMGR_RESET_EN_REGWEN_EN,
+            .sources_enable_reg_offset = PWRMGR_RESET_EN_REG_OFFSET,
+            .cur_req_sources_reg_offset = PWRMGR_RESET_STATUS_REG_OFFSET,
+            .bitfield =
+                {
+                    .mask = kDifPwrmgrResetRequestSourceOne,
+                    .index = 0,
+                },
+        },
+};
+
+/**
+ * Checks if a value is a valid `dif_pwrmgr_toggle_t` and converts it to `bool`.
+ */
+DIF_WARN_UNUSED_RESULT
+static bool toggle_to_bool(dif_pwrmgr_toggle_t val, bool *val_bool) {
+  switch (val) {
+    case kDifPwrmgrToggleEnabled:
+      *val_bool = true;
+      break;
+    case kDifPwrmgrToggleDisabled:
+      *val_bool = false;
+      break;
+    default:
+      return false;
+  }
+  return true;
+}
+
+/**
+ * Converts a `bool` to `dif_pwrmgr_toggle_t`.
+ */
+static dif_pwrmgr_toggle_t bool_to_toggle(bool val) {
+  return val ? kDifPwrmgrToggleEnabled : kDifPwrmgrToggleDisabled;
+}
+
+/**
+ * Checks if a value is a valid `dif_pwrmgr_req_type_t`.
+ */
+DIF_WARN_UNUSED_RESULT
+static bool is_valid_req_type(dif_pwrmgr_req_type_t val) {
+  return val == kDifPwrmgrReqTypeWakeup || val == kDifPwrmgrReqTypeReset;
+}
+
+/**
+ * Checks if a value is a valid `dif_pwrmgr_irq_t`.
+ */
+DIF_WARN_UNUSED_RESULT
+static bool is_valid_irq(dif_pwrmgr_irq_t val) {
+  return val >= 0 && val <= kDifPwrmgrIrqLast;
+}
+
+/**
+ * Checks if a value is valid for, i.e. fits in the mask of, a
+ * `bitfield_field32_t`.
+ */
+DIF_WARN_UNUSED_RESULT
+static bool is_valid_for_bitfield(uint32_t val, bitfield_field32_t bitfield) {
+  return (val & bitfield.mask) == val;
+}
+
+/**
+ * Checks if the control register is locked.
+ *
+ * Control register is locked when low power is enabled and WFI occurs. It is
+ * unlocked when the chip transitions back to active power state.
+ */
+DIF_WARN_UNUSED_RESULT
+static bool control_register_is_locked(const dif_pwrmgr_t *pwrmgr) {
+  // Control register is locked when `PWRMGR_CTRL_CFG_REGWEN_EN` bit is 0.
+  return !bitfield_bit32_read(
+      mmio_region_read32(pwrmgr->params.base_addr,
+                         PWRMGR_CTRL_CFG_REGWEN_REG_OFFSET),
+      PWRMGR_CTRL_CFG_REGWEN_EN);
+}
+
+/**
+ * The configuration registers CONTROL, WAKEUP_EN, and RESET_EN are all written
+ * in the fast clock domain but used in the slow clock domain. Values of these
+ * registers are not propagated across the clock boundary until this function is
+ * called.
+ */
+static void sync_slow_clock_domain_polled(const dif_pwrmgr_t *pwrmgr) {
+  // Start sync and wait for it to finish.
+  mmio_region_write32(pwrmgr->params.base_addr, PWRMGR_CFG_CDC_SYNC_REG_OFFSET,
+                      bitfield_bit32_write(0, PWRMGR_CFG_CDC_SYNC_SYNC, true));
+  while (bitfield_bit32_read(mmio_region_read32(pwrmgr->params.base_addr,
+                                                PWRMGR_CFG_CDC_SYNC_REG_OFFSET),
+                             PWRMGR_CFG_CDC_SYNC_SYNC)) {
+  }
+}
+
+/**
+ * Checks if sources of a request type are locked.
+ */
+DIF_WARN_UNUSED_RESULT
+static bool request_sources_is_locked(const dif_pwrmgr_t *pwrmgr,
+                                      dif_pwrmgr_req_type_t req_type) {
+  request_reg_info_t reg_info = request_reg_infos[req_type];
+  uint32_t reg_val = mmio_region_read32(pwrmgr->params.base_addr,
+                                        reg_info.write_enable_reg_offset);
+  // Locked if write enable bit is set to 0.
+  return !bitfield_bit32_read(reg_val, reg_info.write_enable_bit_index);
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_init(dif_pwrmgr_params_t params,
+                                    dif_pwrmgr_t *pwrmgr) {
+  if (pwrmgr == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  *pwrmgr = (dif_pwrmgr_t){.params = params};
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_config_result_t dif_pwrmgr_low_power_set_enabled(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_toggle_t new_state) {
+  if (pwrmgr == NULL) {
+    return kDifPwrmgrConfigBadArg;
+  }
+
+  bool enable = false;
+  if (!toggle_to_bool(new_state, &enable)) {
+    return kDifPwrmgrConfigBadArg;
+  }
+
+  if (control_register_is_locked(pwrmgr)) {
+    return kDifPwrMgrConfigLocked;
+  }
+
+  uint32_t reg_val =
+      mmio_region_read32(pwrmgr->params.base_addr, PWRMGR_CONTROL_REG_OFFSET);
+  reg_val =
+      bitfield_bit32_write(reg_val, PWRMGR_CONTROL_LOW_POWER_HINT, enable);
+  mmio_region_write32(pwrmgr->params.base_addr, PWRMGR_CONTROL_REG_OFFSET,
+                      reg_val);
+
+  // Slow clock domain must be synced for changes to take effect.
+  sync_slow_clock_domain_polled(pwrmgr);
+
+  return kDifPwrmgrConfigOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_low_power_get_enabled(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_toggle_t *cur_state) {
+  if (pwrmgr == NULL || cur_state == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  uint32_t reg_val =
+      mmio_region_read32(pwrmgr->params.base_addr, PWRMGR_CONTROL_REG_OFFSET);
+  *cur_state = bool_to_toggle(
+      bitfield_bit32_read(reg_val, PWRMGR_CONTROL_LOW_POWER_HINT));
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_config_result_t dif_pwrmgr_set_domain_config(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_domain_config_t config) {
+  if (pwrmgr == NULL || !is_valid_for_bitfield(config, kDomainConfigBitfield)) {
+    return kDifPwrmgrConfigBadArg;
+  }
+
+  if (control_register_is_locked(pwrmgr)) {
+    return kDifPwrMgrConfigLocked;
+  }
+
+  uint32_t reg_val =
+      mmio_region_read32(pwrmgr->params.base_addr, PWRMGR_CONTROL_REG_OFFSET);
+  reg_val = bitfield_field32_write(reg_val, kDomainConfigBitfield, config);
+  mmio_region_write32(pwrmgr->params.base_addr, PWRMGR_CONTROL_REG_OFFSET,
+                      reg_val);
+
+  // Slow clock domain must be synced for changes to take effect.
+  sync_slow_clock_domain_polled(pwrmgr);
+
+  return kDifPwrmgrConfigOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_get_domain_config(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_domain_config_t *config) {
+  if (pwrmgr == NULL || config == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  uint32_t reg_val =
+      mmio_region_read32(pwrmgr->params.base_addr, PWRMGR_CONTROL_REG_OFFSET);
+  *config = bitfield_field32_read(reg_val, kDomainConfigBitfield);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_config_result_t dif_pwrmgr_set_request_sources(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
+    dif_pwrmgr_request_sources_t sources) {
+  if (pwrmgr == NULL || !is_valid_req_type(req_type)) {
+    return kDifPwrmgrConfigBadArg;
+  }
+
+  request_reg_info_t reg_info = request_reg_infos[req_type];
+
+  if (!is_valid_for_bitfield(sources, reg_info.bitfield)) {
+    return kDifPwrmgrConfigBadArg;
+  }
+
+  // Return early if locked.
+  if (request_sources_is_locked(pwrmgr, req_type)) {
+    return kDifPwrMgrConfigLocked;
+  }
+
+  // Write new value
+  uint32_t reg_val = bitfield_field32_write(0, reg_info.bitfield, sources);
+  mmio_region_write32(pwrmgr->params.base_addr,
+                      reg_info.sources_enable_reg_offset, reg_val);
+  // Slow clock domain must be synced for changes to take effect.
+  sync_slow_clock_domain_polled(pwrmgr);
+
+  return kDifPwrmgrConfigOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_get_request_sources(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
+    dif_pwrmgr_request_sources_t *sources) {
+  if (pwrmgr == NULL || !is_valid_req_type(req_type) || sources == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  request_reg_info_t reg_info = request_reg_infos[req_type];
+  uint32_t reg_val = mmio_region_read32(pwrmgr->params.base_addr,
+                                        reg_info.sources_enable_reg_offset);
+  *sources = bitfield_field32_read(reg_val, reg_info.bitfield);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_get_current_request_sources(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
+    dif_pwrmgr_request_sources_t *sources) {
+  if (pwrmgr == NULL || !is_valid_req_type(req_type) || sources == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  request_reg_info_t reg_info = request_reg_infos[req_type];
+  uint32_t reg_val = mmio_region_read32(pwrmgr->params.base_addr,
+                                        reg_info.cur_req_sources_reg_offset);
+  *sources = bitfield_field32_read(reg_val, reg_info.bitfield);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_request_sources_lock(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type) {
+  if (pwrmgr == NULL || !is_valid_req_type(req_type)) {
+    return kDifPwrmgrBadArg;
+  }
+
+  // Only a single bit of this register is significant, thus we don't perform a
+  // read-modify-write. Setting this bit to 0 locks sources.
+  mmio_region_write32(pwrmgr->params.base_addr,
+                      request_reg_infos[req_type].write_enable_reg_offset, 0);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_request_sources_is_locked(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_req_type_t req_type,
+    bool *is_locked) {
+  if (pwrmgr == NULL || !is_valid_req_type(req_type) || is_locked == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  *is_locked = request_sources_is_locked(pwrmgr, req_type);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_wakeup_request_recording_set_enabled(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_toggle_t new_state) {
+  if (pwrmgr == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  bool enable = false;
+  if (!toggle_to_bool(new_state, &enable)) {
+    return kDifPwrmgrBadArg;
+  }
+
+  // Only a single bit of this register is significant, thus we don't perform a
+  // read-modify-write. Setting this bit to 1 disables recording.
+  uint32_t reg_val =
+      bitfield_bit32_write(0, PWRMGR_WAKE_INFO_CAPTURE_DIS_VAL, !enable);
+
+  mmio_region_write32(pwrmgr->params.base_addr,
+                      PWRMGR_WAKE_INFO_CAPTURE_DIS_REG_OFFSET, reg_val);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_wakeup_request_recording_get_enabled(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_toggle_t *cur_state) {
+  if (pwrmgr == NULL || cur_state == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  uint32_t reg_val = mmio_region_read32(
+      pwrmgr->params.base_addr, PWRMGR_WAKE_INFO_CAPTURE_DIS_REG_OFFSET);
+  // Recording is disabled if this bit is set to 1.
+  *cur_state = bool_to_toggle(
+      !bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_CAPTURE_DIS_VAL));
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_wakeup_reason_get(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_wakeup_reason_t *reason) {
+  if (pwrmgr == NULL || reason == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  uint32_t reg_val =
+      mmio_region_read32(pwrmgr->params.base_addr, PWRMGR_WAKE_INFO_REG_OFFSET);
+
+  dif_pwrmgr_wakeup_types_t types = 0;
+  if (bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_FALL_THROUGH)) {
+    types |= kDifPwrmgrWakeupTypeFallThrough;
+  }
+  if (bitfield_bit32_read(reg_val, PWRMGR_WAKE_INFO_ABORT)) {
+    types |= kDifPwrmgrWakeupTypeAbort;
+  }
+
+  uint32_t request_sources = bitfield_field32_read(
+      reg_val, request_reg_infos[kDifPwrmgrReqTypeWakeup].bitfield);
+  if (request_sources != 0) {
+    types |= kDifPwrmgrWakeupTypeRequest;
+  }
+
+  *reason = (dif_pwrmgr_wakeup_reason_t){
+      .types = types,
+      .request_sources = request_sources,
+  };
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_wakeup_reason_clear(const dif_pwrmgr_t *pwrmgr) {
+  if (pwrmgr == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  mmio_region_write32(pwrmgr->params.base_addr, PWRMGR_WAKE_INFO_REG_OFFSET,
+                      UINT32_MAX);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_irq_is_pending(const dif_pwrmgr_t *pwrmgr,
+                                              dif_pwrmgr_irq_t irq,
+                                              bool *is_pending) {
+  if (pwrmgr == NULL || !is_valid_irq(irq) || is_pending == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  uint32_t reg_val = mmio_region_read32(pwrmgr->params.base_addr,
+                                        PWRMGR_INTR_STATE_REG_OFFSET);
+  *is_pending = bitfield_bit32_read(reg_val, irq);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_irq_acknowledge(const dif_pwrmgr_t *pwrmgr,
+                                               dif_pwrmgr_irq_t irq) {
+  if (pwrmgr == NULL || !is_valid_irq(irq)) {
+    return kDifPwrmgrBadArg;
+  }
+
+  uint32_t reg_val = bitfield_bit32_write(0, irq, true);
+  mmio_region_write32(pwrmgr->params.base_addr, PWRMGR_INTR_STATE_REG_OFFSET,
+                      reg_val);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_irq_get_enabled(const dif_pwrmgr_t *pwrmgr,
+                                               dif_pwrmgr_irq_t irq,
+                                               dif_pwrmgr_toggle_t *state) {
+  if (pwrmgr == NULL || !is_valid_irq(irq) || state == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  uint32_t reg_val = mmio_region_read32(pwrmgr->params.base_addr,
+                                        PWRMGR_INTR_ENABLE_REG_OFFSET);
+  *state = bool_to_toggle(bitfield_bit32_read(reg_val, irq));
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_irq_set_enabled(const dif_pwrmgr_t *pwrmgr,
+                                               dif_pwrmgr_irq_t irq,
+                                               dif_pwrmgr_toggle_t state) {
+  if (pwrmgr == NULL || !is_valid_irq(irq)) {
+    return kDifPwrmgrBadArg;
+  }
+
+  bool enable = false;
+  if (!toggle_to_bool(state, &enable)) {
+    return kDifPwrmgrBadArg;
+  }
+
+  uint32_t reg_val = mmio_region_read32(pwrmgr->params.base_addr,
+                                        PWRMGR_INTR_ENABLE_REG_OFFSET);
+  reg_val = bitfield_bit32_write(reg_val, irq, enable);
+  mmio_region_write32(pwrmgr->params.base_addr, PWRMGR_INTR_ENABLE_REG_OFFSET,
+                      reg_val);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_irq_force(const dif_pwrmgr_t *pwrmgr,
+                                         dif_pwrmgr_irq_t irq) {
+  if (pwrmgr == NULL || !is_valid_irq(irq)) {
+    return kDifPwrmgrBadArg;
+  }
+
+  uint32_t reg_val = bitfield_bit32_write(0, irq, true);
+  mmio_region_write32(pwrmgr->params.base_addr, PWRMGR_INTR_TEST_REG_OFFSET,
+                      reg_val);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_irq_disable_all(
+    const dif_pwrmgr_t *pwrmgr, dif_pwrmgr_irq_snapshot_t *snapshot) {
+  if (pwrmgr == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  if (snapshot != NULL) {
+    *snapshot = mmio_region_read32(pwrmgr->params.base_addr,
+                                   PWRMGR_INTR_ENABLE_REG_OFFSET);
+  }
+  mmio_region_write32(pwrmgr->params.base_addr, PWRMGR_INTR_ENABLE_REG_OFFSET,
+                      0);
+
+  return kDifPwrmgrOk;
+}
+
+dif_pwrmgr_result_t dif_pwrmgr_irq_restore_all(
+    const dif_pwrmgr_t *pwrmgr, const dif_pwrmgr_irq_snapshot_t *snapshot) {
+  if (pwrmgr == NULL || snapshot == NULL) {
+    return kDifPwrmgrBadArg;
+  }
+
+  mmio_region_write32(pwrmgr->params.base_addr, PWRMGR_INTR_ENABLE_REG_OFFSET,
+                      *snapshot);
+  return kDifPwrmgrOk;
+}