Software APIs
pwrmgr_sleep_all_wake_ups_impl.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 // Contains code that is common to deep, normal, and random sleep for
6 // pwrmgr all_wake_ups test.
7 
8 #include "sw/device/tests/pwrmgr_sleep_all_wake_ups_impl.h"
9 
10 #include "dt/dt_aon_timer.h"
16 #include "sw/device/lib/testing/aon_timer_testutils.h"
17 #include "sw/device/lib/testing/pwrmgr_testutils.h"
18 #include "sw/device/lib/testing/rv_plic_testutils.h"
19 
20 #ifdef HAS_ADC_CTRL
21 #include "dt/dt_adc_ctrl.h"
23 #endif
24 #ifdef HAS_SENSOR_CTRL
25 #include "dt/dt_sensor_ctrl.h"
27 
28 #include "sensor_ctrl_regs.h"
29 #endif
30 #ifdef HAS_SYSRST_CTRL
31 #include "dt/dt_sysrst_ctrl.h"
33 #endif
34 #ifdef HAS_USBDEV
35 #include "dt/dt_usbdev.h"
37 #endif
38 
39 static const uint32_t kPinmuxWkupDetector5 = 5;
40 static const uint32_t kSensorCtrlEventIdx = 0;
41 
42 enum {
43  kPlicTarget = 0,
44 };
45 
46 static const dt_pwrmgr_t kPwrmgrDt = 0;
47 static_assert(kDtPwrmgrCount == 1, "this library expects exactly one pwrmgr");
48 static const dt_pinmux_t kPinmuxDt = 0;
49 static_assert(kDtPinmuxCount == 1, "this library expects exactly one pinmux");
50 static const dt_rv_plic_t kRvPlicDt = 0;
51 static_assert(kDtRvPlicCount == 1, "this library expects exactly one rv_plic");
52 
53 dif_pwrmgr_t pwrmgr;
54 dif_rv_plic_t rv_plic;
55 
56 #define INIT_DIF_DT(__mod_name, __src, __difname) \
57  dt_##__mod_name##_t __dt = dt_##__mod_name##_from_instance_id(src.inst_id); \
58  dif_##__mod_name##_t __difname; \
59  CHECK_DIF_OK(dif_##__mod_name##_init_from_dt(__dt, &__difname));
60 
61 #ifdef HAS_SYSRST_CTRL
62 /**
63  * sysrst_ctrl config for test #1
64  * . set sysrst_ctrl.KEY_INTR_CTL.pwrb_in_H2L to 1
65  * . use IOR13 as pwrb_in for DV, and IOC0 otherwise
66  */
67 static void sysrst_ctrl_wakeup_config(dt_pwrmgr_wakeup_src_t src) {
68  INIT_DIF_DT(sysrst_ctrl, src, sysrst_ctrl)
71  .debounce_time_threshold = 1, // 5us
72  };
73  CHECK_DIF_OK(
74  dif_sysrst_ctrl_input_change_detect_configure(&sysrst_ctrl, config));
75  dif_pinmux_t pinmux;
76  CHECK_DIF_OK(dif_pinmux_init_from_dt(kPinmuxDt, &pinmux));
77  CHECK_DIF_OK(dif_pinmux_input_select(
81 }
82 
83 static void sysrst_ctrl_wakeup_check(dt_pwrmgr_wakeup_src_t src) {
84  INIT_DIF_DT(sysrst_ctrl, src, sysrst_ctrl)
85  bool has_wakeup = false;
86  CHECK_DIF_OK(
87  dif_sysrst_ctrl_ulp_wakeup_get_status(&sysrst_ctrl, &has_wakeup));
88  CHECK(has_wakeup, "Expected sysrst_ctrl wakeup to be set");
89 }
90 
91 static void sysrst_ctrl_wakeup_clear(dt_pwrmgr_wakeup_src_t src) {
92  INIT_DIF_DT(sysrst_ctrl, src, sysrst_ctrl)
93  CHECK_DIF_OK(dif_sysrst_ctrl_ulp_wakeup_clear_status(&sysrst_ctrl));
94  // Disable wakeups.
96  .input_changes = 0,
97  .debounce_time_threshold = 0, // 5us
98  };
99  CHECK_DIF_OK(
100  dif_sysrst_ctrl_input_change_detect_configure(&sysrst_ctrl, config));
101 }
102 #endif /* HAS_SYSRST_CTRL */
103 
104 #ifdef HAS_ADC_CTRL
105 
106 static bool adc_ctrl_skip(dt_pwrmgr_wakeup_src_t src) {
107  return kDeviceType != kDeviceSimDV;
108 }
109 
110 /**
111  * adc_ctrl config for test #2
112  * . enable filter 5 and set voltage range (0,200)
113  */
114 static void adc_ctrl_wakeup_config(dt_pwrmgr_wakeup_src_t src) {
115  INIT_DIF_DT(adc_ctrl, src, adc_ctrl)
116  dif_adc_ctrl_config_t cfg = {
118  .power_up_time_aon_cycles = 7,
119  .wake_up_time_aon_cycles = 100,
120  .num_low_power_samples = 2,
121  .num_normal_power_samples = 8,
122  };
123  CHECK_DIF_OK(dif_adc_ctrl_configure(&adc_ctrl, cfg));
124 
125  dif_adc_ctrl_filter_config_t filter_cfg = {
126  .filter = kDifAdcCtrlFilter5,
127  .min_voltage = 0,
128  .max_voltage = 200,
129  .in_range = true,
130  .generate_wakeup_on_match = true,
131  .generate_irq_on_match = false,
132  };
133 
134  CHECK_DIF_OK(dif_adc_ctrl_configure_filter(&adc_ctrl, kDifAdcCtrlChannel0,
135  filter_cfg, kDifToggleEnabled));
136  CHECK_DIF_OK(dif_adc_ctrl_configure_filter(&adc_ctrl, kDifAdcCtrlChannel1,
137  filter_cfg, kDifToggleEnabled));
139  &adc_ctrl, kDifAdcCtrlFilter5, kDifToggleEnabled));
140  CHECK_DIF_OK(dif_adc_ctrl_set_enabled(&adc_ctrl, kDifToggleEnabled));
141 }
142 
143 static void adc_ctrl_wakeup_check(dt_pwrmgr_wakeup_src_t src) {
144  INIT_DIF_DT(adc_ctrl, src, adc_ctrl)
145  uint32_t filter_status = 0;
146  CHECK_DIF_OK(
147  dif_adc_ctrl_wait_cdc_sync(&adc_ctrl, (uint32_t)kClockFreqAonHz));
148  CHECK_DIF_OK(dif_adc_ctrl_get_filter_status(&adc_ctrl, &filter_status));
149  CHECK(filter_status == ((1 << kDifAdcCtrlFilter5) | (1 << kDifAdcCtrlTrans)),
150  "Expected bits %d and %d set in filter status, got status 0x%x",
151  kDifAdcCtrlFilter5, kDifAdcCtrlTrans, filter_status);
152 }
153 
154 static void adc_ctrl_wakeup_clear(dt_pwrmgr_wakeup_src_t src) {
155  INIT_DIF_DT(adc_ctrl, src, adc_ctrl)
157  &adc_ctrl, kDifAdcCtrlFilter5, kDifToggleDisabled));
158 }
159 #endif /* HAS_ADC_CTRL */
160 
161 /**
162  * pinmux config for test #3
163  * . use IOB7 as an input for DV, IOC0 otherwise
164  * . set posedge detection
165  */
166 static void pinmux_wakeup_config(dt_pwrmgr_wakeup_src_t src) {
167  INIT_DIF_DT(pinmux, src, pinmux)
168 #ifdef OPENTITAN_IS_EARLGREY
169  // Make sure the pin has a pulldown before we enable it for wakeup.
170  // FPGA doesn't implement pullup/down, so just use that attribute for SimDV.
174 #else
175 #error Unsupported top, please provide a pin configuration
176 #endif
177  dif_pinmux_wakeup_config_t detector_cfg = {
179  .pad_type = kDifPinmuxPadKindMio,
180  .pad_select = wakeup_pin,
182  .counter_threshold = 0 /* Don't need for posedge detection */,
183  };
184  if (kDeviceType != kDeviceSimDV) {
185  dif_pinmux_pad_attr_t out_attr;
186  dif_pinmux_pad_attr_t in_attr = {
187  .slew_rate = 0,
188  .drive_strength = 0,
189  .flags = kDeviceType == kDeviceSimDV
190  ? kDifPinmuxPadAttrPullResistorEnable
191  : 0};
192  CHECK_DIF_OK(dif_pinmux_pad_write_attrs(
193  &pinmux, wakeup_pin, kDifPinmuxPadKindMio, in_attr, &out_attr));
194  }
195  CHECK_DIF_OK(dif_pinmux_wakeup_detector_enable(&pinmux, kPinmuxWkupDetector5,
196  detector_cfg));
197 }
198 
199 static void pinmux_wakeup_check(dt_pwrmgr_wakeup_src_t src) {
200  INIT_DIF_DT(pinmux, src, pinmux)
201  uint32_t wakeup_cause;
202  CHECK_DIF_OK(dif_pinmux_wakeup_cause_get(&pinmux, &wakeup_cause));
203  CHECK(wakeup_cause == 1 << kPinmuxWkupDetector5,
204  "Expected pinmux wakeup cause 5");
205 }
206 
207 static void pinmux_wakeup_clear(dt_pwrmgr_wakeup_src_t src) {
208  INIT_DIF_DT(pinmux, src, pinmux)
209  CHECK_DIF_OK(dif_pinmux_wakeup_cause_clear(&pinmux));
210 }
211 
212 #ifdef HAS_USBDEV
213 /**
214  * usb config for test #4
215  * . Fake low power entry through usb
216  * . Force usb to output suspend indication
217  * (*dif) handle is not used but leave as is
218  * to be called from execute_test
219  */
220 static void usb_wakeup_config(dt_pwrmgr_wakeup_src_t src) {
221  // Despite the name, the wakeup source is the pinmux.
222  dif_usbdev_t usbdev;
223  static const dt_usbdev_t dt = 0;
224  static_assert(kDtUsbdevCount == 1, "expect exactly one usbdev");
225  CHECK_DIF_OK(dif_usbdev_init_from_dt(dt, &usbdev));
226 
228  .dp_pullup_en = true,
229  .dn_pullup_en = false,
230  };
231  CHECK_DIF_OK(dif_usbdev_set_phy_pins_state(&usbdev, kDifToggleEnabled, pins));
232  CHECK_DIF_OK(dif_usbdev_set_wake_enable(&usbdev, kDifToggleEnabled));
233 
234  LOG_INFO("usb_wakeup_config: wait 20us (usb)");
235  // Give the hardware a chance to recognize the wakeup values are the same.
236  busy_spin_micros(20); // 20us
237 }
238 
239 static void usb_wakeup_check(dt_pwrmgr_wakeup_src_t src) {
240  // No bit in USBDEV indicates it caused a wakeup.
241 }
242 
243 static void usb_wakeup_clear(dt_pwrmgr_wakeup_src_t src) {
244  INIT_DIF_DT(pinmux, src, pinmux)
245  dif_usbdev_t usbdev;
246  static const dt_usbdev_t dt_usbdev = 0;
247  static_assert(kDtUsbdevCount == 1, "expect exactly one usbdev");
248  CHECK_DIF_OK(dif_usbdev_init_from_dt(dt_usbdev, &usbdev));
249 
250  CHECK_DIF_OK(dif_usbdev_set_wake_enable(&usbdev, kDifToggleDisabled));
251  // Write again to make sure the first one has already completed.
252  CHECK_DIF_OK(dif_usbdev_set_wake_enable(&usbdev, kDifToggleDisabled));
253  CHECK_DIF_OK(dif_pinmux_wakeup_cause_clear(&pinmux));
254 }
255 #endif /* HAS_USBDEV */
256 
257 /**
258  * aon timer config for test #5
259  * set wakeup signal in 50us
260  */
261 static void aontimer_wakeup_config(dt_pwrmgr_wakeup_src_t src) {
262  INIT_DIF_DT(aon_timer, src, aon_timer)
263  CHECK_STATUS_OK(aon_timer_testutils_wakeup_config(&aon_timer, 10));
264 }
265 
266 static void aontimer_wakeup_check(dt_pwrmgr_wakeup_src_t src) {
267  INIT_DIF_DT(aon_timer, src, aon_timer)
268  bool cause = false;
269  CHECK_DIF_OK(dif_aon_timer_get_wakeup_cause(&aon_timer, &cause));
270  CHECK(cause, "Expected aontimer wakeup cause to be enabled");
271 }
272 
273 static void aontimer_wakeup_clear(dt_pwrmgr_wakeup_src_t src) {
274  INIT_DIF_DT(aon_timer, src, aon_timer)
275  CHECK_DIF_OK(dif_aon_timer_wakeup_stop(&aon_timer));
276  // Write again to make sure the first one has already completed.
277  CHECK_DIF_OK(dif_aon_timer_wakeup_stop(&aon_timer));
278  CHECK_DIF_OK(dif_aon_timer_clear_wakeup_cause(&aon_timer));
279 }
280 
281 #ifdef HAS_SENSOR_CTRL
282 /**
283  * sensor ctrl config for test #6
284  * setup event trigger0
285  */
286 static void sensor_ctrl_wakeup_config(dt_pwrmgr_wakeup_src_t src) {
287  INIT_DIF_DT(sensor_ctrl, src, sensor_ctrl)
288  // Enable all AST alerts in sensor_ctrl
289  for (uint32_t k = 0; k < SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS; k++) {
290  CHECK_DIF_OK(
292  }
294  &sensor_ctrl, kSensorCtrlEventIdx, kDifToggleEnabled));
295 }
296 
297 static void sensor_ctrl_wakeup_check(dt_pwrmgr_wakeup_src_t src) {
298  INIT_DIF_DT(sensor_ctrl, src, sensor_ctrl)
300  dif_toggle_t enable;
302  &sensor_ctrl, kSensorCtrlEventIdx, &enable));
303  CHECK(enable == kDifToggleEnabled, "Expected event trigger enabled");
304  CHECK_DIF_OK(dif_sensor_ctrl_get_recov_events(&sensor_ctrl, &events));
305  CHECK(events & (1 << kSensorCtrlEventIdx), "Expected bit %d to be set",
306  kSensorCtrlEventIdx);
307 }
308 
309 static void sensor_ctrl_wakeup_clear(dt_pwrmgr_wakeup_src_t src) {
310  INIT_DIF_DT(sensor_ctrl, src, sensor_ctrl)
311  // clear event trigger
313  &sensor_ctrl, kSensorCtrlEventIdx, kDifToggleDisabled));
314  CHECK_DIF_OK(
315  dif_sensor_ctrl_clear_recov_event(&sensor_ctrl, kSensorCtrlEventIdx));
316  dif_toggle_t enable;
318  &sensor_ctrl, kSensorCtrlEventIdx, &enable));
319  CHECK(enable == kDifToggleDisabled, "Expected event trigger disabled");
321  CHECK_DIF_OK(dif_sensor_ctrl_get_recov_events(&sensor_ctrl, &events));
322  CHECK(events == 0, "Expected recoverable events to be clear, got 0x%x",
323  events);
324 }
325 #endif /* HAS_SENSOR_CTRL */
326 
327 const test_wakeup_sources_t kTestWakeupSources[] = {
328 #ifdef HAS_SYSRST_CTRL
329  {
330  .name = "SYSRST_CTRL",
331  .dev_type = kDtDeviceTypeSysrstCtrl,
332  .wakeup = kDtSysrstCtrlWakeupWkupReq,
333  .skip = NULL,
334  .config = sysrst_ctrl_wakeup_config,
335  .check = sysrst_ctrl_wakeup_check,
336  .clear = sysrst_ctrl_wakeup_clear,
337  },
338 #endif /* HAS_SYSRST_CTRL */
339 #ifdef HAS_ADC_CTRL
340  {
341  .name = "ADC_CTRL",
342  .dev_type = kDtDeviceTypeAdcCtrl,
343  .wakeup = kDtAdcCtrlWakeupWkupReq,
344  .skip = adc_ctrl_skip,
345  .config = adc_ctrl_wakeup_config,
346  .check = adc_ctrl_wakeup_check,
347  .clear = adc_ctrl_wakeup_clear,
348  },
349 #endif /* HAS_ADC_CTRL */
350  {
351  .name = "PINMUX",
352  .dev_type = kDtDeviceTypePinmux,
353  .wakeup = kDtPinmuxWakeupPinWkupReq,
354  .skip = NULL,
355  .config = pinmux_wakeup_config,
356  .check = pinmux_wakeup_check,
357  .clear = pinmux_wakeup_clear,
358  },
359 #ifdef HAS_USBDEV
360  {
361  .name = "USB",
362  .dev_type = kDtDeviceTypePinmux,
363  .wakeup = kDtPinmuxWakeupUsbWkupReq,
364  .skip = NULL,
365  .config = usb_wakeup_config,
366  .check = usb_wakeup_check,
367  .clear = usb_wakeup_clear,
368  },
369 #endif /* HAS_USBDEV */
370  {
371  .name = "AONTIMER",
372  .dev_type = kDtDeviceTypeAonTimer,
373  .wakeup = kDtAonTimerWakeupWkupReq,
374  .skip = NULL,
375  .config = aontimer_wakeup_config,
376  .check = aontimer_wakeup_check,
377  .clear = aontimer_wakeup_clear,
378  },
379 #ifdef HAS_SENSOR_CTRL
380  {
381  .name = "SENSOR_CTRL",
382  .dev_type = kDtDeviceTypeSensorCtrl,
383  .wakeup = kDtSensorCtrlWakeupWkupReq,
384  .skip = NULL,
385  .config = sensor_ctrl_wakeup_config,
386  .check = sensor_ctrl_wakeup_check,
387  .clear = sensor_ctrl_wakeup_clear,
388  },
389 #endif /* HAS_SENSOR_CTRL */
390 };
391 
392 const test_wakeup_sources_t *get_wakeup_source(
393  size_t wakeup_unit, dt_pwrmgr_wakeup_src_t *out_src) {
394  dt_pwrmgr_wakeup_src_t src = dt_pwrmgr_wakeup_src(kPwrmgrDt, wakeup_unit);
395  if (out_src) {
396  *out_src = src;
397  }
398  for (size_t idx = 0; idx < ARRAYSIZE(kTestWakeupSources); idx++) {
399  if (dt_device_type(src.inst_id) == kTestWakeupSources[idx].dev_type &&
400  src.wakeup == kTestWakeupSources[idx].wakeup) {
401  return &kTestWakeupSources[idx];
402  }
403  }
404  LOG_ERROR("unable to test wakeup source %d (inst_id=%d, wkup=%d)",
405  wakeup_unit, src.inst_id, src.wakeup);
406  return NULL;
407 }
408 
409 void init_units(void) {
410  CHECK_DIF_OK(dif_pwrmgr_init_from_dt(kPwrmgrDt, &pwrmgr));
411  CHECK_DIF_OK(dif_rv_plic_init_from_dt(kRvPlicDt, &rv_plic));
412  // Enable all the AON interrupts used in this test.
413  dif_rv_plic_irq_id_t irq_id =
414  dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup);
415  rv_plic_testutils_irq_range_enable(&rv_plic, kPlicTarget, irq_id, irq_id);
416  // Enable pwrmgr interrupt
417  CHECK_DIF_OK(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled));
418 }
419 
420 size_t get_wakeup_count(void) { return dt_pwrmgr_wakeup_src_count(kPwrmgrDt); }
421 
422 bool execute_test(size_t wakeup_unit, bool deep_sleep) {
423  dt_pwrmgr_wakeup_src_t wakeup;
424  const test_wakeup_sources_t *src = get_wakeup_source(wakeup_unit, &wakeup);
425  CHECK(src, "cannot execute test");
426 
427  if (src->skip && src->skip(wakeup)) {
428  LOG_INFO("Skip %d (%s)", wakeup_unit, src->name);
429  return false;
430  }
431 
432  // This message is used by the harness to know how to wakeup the device.
433  LOG_INFO("Test %d begin (%s)", wakeup_unit, src->name);
434 
435  // Configure wakeup device
436  src->config(wakeup);
438  CHECK_DIF_OK(dif_pwrmgr_get_domain_config(&pwrmgr, &cfg));
439  if (deep_sleep) {
440  cfg &= ~kDifPwrmgrDomainOptionMainPowerInLowPower;
441  } else {
442  cfg |= kDifPwrmgrDomainOptionMainPowerInLowPower;
443  }
444  CHECK_STATUS_OK(
445  pwrmgr_testutils_enable_low_power(&pwrmgr, 1 << wakeup_unit, cfg));
446  LOG_INFO("Issue WFI to enter sleep %d", wakeup_unit);
448  return true;
449 }
450 
451 void check_wakeup_reason(size_t wakeup_unit) {
452  dt_pwrmgr_wakeup_src_t wakeup;
453  const test_wakeup_sources_t *src = get_wakeup_source(wakeup_unit, &wakeup);
454  CHECK(src, "cannot execute test");
455 
456  dif_pwrmgr_wakeup_reason_t wakeup_reason;
457  CHECK_DIF_OK(dif_pwrmgr_wakeup_reason_get(&pwrmgr, &wakeup_reason));
458  CHECK(UNWRAP(pwrmgr_testutils_is_wakeup_reason(&pwrmgr, 1 << wakeup_unit)),
459  "wakeup reason wrong exp:%x obs:%x", wakeup_unit, wakeup_reason);
460 
461  src->check(wakeup);
462 }
463 
464 static bool get_wakeup_status(void) {
465  dif_pwrmgr_request_sources_t wake_req = ~0u;
467  &pwrmgr, kDifPwrmgrReqTypeWakeup, &wake_req));
468  return (wake_req > 0);
469 }
470 
471 void clear_wakeup(size_t wakeup_unit) {
472  dt_pwrmgr_wakeup_src_t wakeup;
473  const test_wakeup_sources_t *src = get_wakeup_source(wakeup_unit, &wakeup);
474  CHECK(src, "cannot execute test");
475 
476  src->clear(wakeup);
477  // Ensure the de-asserted events have cleared from the wakeup pipeline
478  // within 100us.
479  IBEX_SPIN_FOR(!get_wakeup_status(), 100);
480  CHECK_DIF_OK(dif_pwrmgr_wakeup_reason_clear(&pwrmgr));
481 }
482 
483 /**
484  * External interrupt handler.
485  */
486 bool ottf_handle_irq(uint32_t *exc_info, dt_instance_id_t devid,
487  dif_rv_plic_irq_id_t irq_id) {
488  if (devid == dt_pwrmgr_instance_id(kPwrmgrDt) &&
489  irq_id == dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup)) {
490  CHECK_DIF_OK(dif_pwrmgr_irq_acknowledge(&pwrmgr, kDtPwrmgrIrqWakeup));
491  return true;
492  } else {
493  return false;
494  }
495 }