Software APIs
pwrmgr_lowpower_cancel_test.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 
11 #include "sw/device/lib/runtime/irq.h"
13 #include "sw/device/lib/testing/aon_timer_testutils.h"
14 #include "sw/device/lib/testing/pwrmgr_testutils.h"
15 #include "sw/device/lib/testing/rv_plic_testutils.h"
16 #include "sw/device/lib/testing/test_framework/check.h"
18 
19 #include "pwrmgr_regs.h"
20 
21 enum {
22  kPlicTarget = 0,
23 };
24 
25 static dif_pwrmgr_t pwrmgr;
26 static dif_rv_plic_t rv_plic;
27 static dif_aon_timer_t timer;
28 
29 static const dt_pwrmgr_t kPwrmgrDt = 0;
30 static_assert(kDtPwrmgrCount == 1, "this test expects a pwrmgr");
31 static const dt_aon_timer_t kAonTimerDt = 0;
32 static_assert(kDtAonTimerCount == 1, "this test expects an aon_timer");
33 static const dt_rv_plic_t kRvPlicDt = 0;
34 static_assert(kDtRvPlicCount == 1, "this test expects exactly one rv_plic");
35 
36 static dif_pwrmgr_request_sources_t wakeup_src;
37 
38 bool ottf_handle_irq(uint32_t *exc_info, dt_instance_id_t devid,
39  dif_rv_plic_irq_id_t irq_id) {
40  if (devid == dt_aon_timer_instance_id(kAonTimerDt)) {
41  LOG_INFO("AON Timer ISR");
42  dif_aon_timer_irq_t irq =
43  dt_aon_timer_irq_from_plic_id(kAonTimerDt, irq_id);
44 
45  if (irq == kDtAonTimerIrqWkupTimerExpired) {
46  CHECK_DIF_OK(dif_aon_timer_wakeup_stop(&timer));
47  } else if (irq == kDtAonTimerIrqWdogTimerBark) {
48  CHECK_DIF_OK(dif_aon_timer_watchdog_stop(&timer));
49  }
50  CHECK_DIF_OK(dif_aon_timer_irq_acknowledge(&timer, irq));
51  bool is_pending = true;
52  CHECK_DIF_OK(dif_aon_timer_irq_is_pending(
53  &timer, kDifAonTimerIrqWkupTimerExpired, &is_pending));
54  CHECK(!is_pending);
55  return true;
56  } else if (devid == dt_pwrmgr_instance_id(kPwrmgrDt)) {
57  LOG_INFO("Pwrmgr ISR");
58  CHECK(irq_id == dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup),
59  "Pwrmgr IRQ ID: %d is incorrect", irq_id);
60  CHECK_DIF_OK(dif_pwrmgr_irq_acknowledge(&pwrmgr, kDtPwrmgrIrqWakeup));
61  return true;
62  } else {
63  return false;
64  }
65 }
66 
67 static bool get_wakeup_status(void) {
68  dif_pwrmgr_request_sources_t wake_req = ~0u;
70  &pwrmgr, kDifPwrmgrReqTypeWakeup, &wake_req));
71  return (wake_req > 0);
72 }
73 
74 static void clear_wakeup(void) {
75  CHECK_DIF_OK(dif_aon_timer_clear_wakeup_cause(&timer));
76  // Ensure the de-asserted events have cleared from the wakeup pipeline
77  // within 100us.
78  IBEX_SPIN_FOR(!get_wakeup_status(), 100);
79  CHECK_DIF_OK(dif_pwrmgr_wakeup_reason_clear(&pwrmgr));
80 }
81 
82 static void test_init(void) {
83  irq_global_ctrl(true);
84  irq_external_ctrl(true);
85 
86  CHECK_DIF_OK(dif_pwrmgr_init_from_dt(kPwrmgrDt, &pwrmgr));
87  CHECK_DIF_OK(dif_rv_plic_init_from_dt(kRvPlicDt, &rv_plic));
88 
89  CHECK_DIF_OK(
91  dt_aon_timer_instance_id(kDtAonTimerAon),
92  kDtAonTimerWakeupWkupReq, &wakeup_src));
93 
94  // Enable AON interrupts.
95  dif_rv_plic_irq_id_t plic_id =
96  dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup);
97  rv_plic_testutils_irq_range_enable(&rv_plic, kPlicTarget, plic_id, plic_id);
98  rv_plic_testutils_irq_range_enable(
99  &rv_plic, kPlicTarget,
100  dt_aon_timer_irq_to_plic_id(kAonTimerDt, kDtAonTimerIrqWkupTimerExpired),
101  dt_aon_timer_irq_to_plic_id(kAonTimerDt, kDtAonTimerIrqWdogTimerBark));
102 
103  // Enable pwrmgr interrupt
104  CHECK_DIF_OK(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled));
105 
106  CHECK_DIF_OK(dif_aon_timer_init_from_dt(kAonTimerDt, &timer));
107  CHECK_DIF_OK(dif_aon_timer_wakeup_stop(&timer));
108 }
109 
110 static void set_timer(uint64_t time) {
111  uint32_t cycles = 0;
112  CHECK_STATUS_OK(aon_timer_testutils_get_aon_cycles_32_from_us(time, &cycles));
113  CHECK_STATUS_OK(aon_timer_testutils_wakeup_config(&timer, cycles));
114 }
115 
116 static bool lowpower_hint_is_cleared(void) {
117  dif_toggle_t low_power_enabled = kDifToggleEnabled;
118  CHECK_DIF_OK(dif_pwrmgr_low_power_get_enabled(&pwrmgr, &low_power_enabled));
119  return low_power_enabled == kDifToggleDisabled;
120 }
121 
122 static void test_sleep(bool wfi_fallthrough) {
123  LOG_INFO("Low power WFI (fallthrough=%d)", wfi_fallthrough);
124 
125  dif_pwrmgr_domain_config_t domain_config =
126  kDifPwrmgrDomainOptionMainPowerInLowPower;
127  CHECK_STATUS_OK(
128  pwrmgr_testutils_enable_low_power(&pwrmgr, wakeup_src, domain_config));
129  irq_global_ctrl(false);
130  if (wfi_fallthrough) {
131  LOG_INFO("Fallthough WFI due to timer pending");
132  set_timer(100);
133  busy_spin_micros(200);
134  } else {
135  set_timer(100);
136  }
138  LOG_INFO("Woke up by source %x", wakeup_src);
141  IBEX_SPIN_FOR(lowpower_hint_is_cleared(), 100);
142  irq_global_ctrl(true);
143  clear_wakeup();
144 
145  LOG_INFO("Normal WFI");
146  // Now do WFI without the LOW_POWER_HINT.
147  irq_global_ctrl(false);
148  set_timer(100);
150  irq_global_ctrl(true);
151 }
152 
153 OTTF_DEFINE_TEST_CONFIG();
154 
155 bool test_main(void) {
156  test_init();
157  if (UNWRAP(pwrmgr_testutils_is_wakeup_reason(&pwrmgr, 0)) != true) {
158  return false;
159  }
160  test_sleep(false);
161  test_sleep(true);
162  return true;
163 }