|  | // 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/base/mmio.h" | 
|  | #include "sw/device/lib/dif/dif_pwrmgr.h" | 
|  | #include "sw/device/lib/dif/dif_rv_plic.h" | 
|  | #include "sw/device/lib/dif/dif_sensor_ctrl.h" | 
|  | #include "sw/device/lib/runtime/ibex.h" | 
|  | #include "sw/device/lib/runtime/irq.h" | 
|  | #include "sw/device/lib/runtime/log.h" | 
|  | #include "sw/device/lib/testing/pwrmgr_testutils.h" | 
|  | #include "sw/device/lib/testing/rv_plic_testutils.h" | 
|  | #include "sw/device/lib/testing/test_framework/check.h" | 
|  | #include "sw/device/lib/testing/test_framework/ottf_main.h" | 
|  |  | 
|  | #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" | 
|  | #include "sensor_ctrl_regs.h"  // Generated. | 
|  | #include "sw/device/lib/testing/autogen/isr_testutils.h" | 
|  |  | 
|  | OTTF_DEFINE_TEST_CONFIG(); | 
|  |  | 
|  | static dif_pwrmgr_t pwrmgr; | 
|  | static dif_rv_plic_t plic; | 
|  | static plic_isr_ctx_t plic_ctx = {.rv_plic = &plic, | 
|  | .hart_id = kTopEarlgreyPlicTargetIbex0}; | 
|  |  | 
|  | static pwrmgr_isr_ctx_t pwrmgr_isr_ctx = { | 
|  | .pwrmgr = &pwrmgr, | 
|  | .plic_pwrmgr_start_irq_id = kTopEarlgreyPlicIrqIdPwrmgrAonWakeup, | 
|  | .expected_irq = kDifPwrmgrIrqWakeup, | 
|  | .is_only_irq = true}; | 
|  |  | 
|  | static bool get_wakeup_status(void) { | 
|  | dif_pwrmgr_request_sources_t wake_req = -1; | 
|  | CHECK_DIF_OK(dif_pwrmgr_get_current_request_sources( | 
|  | &pwrmgr, kDifPwrmgrReqTypeWakeup, &wake_req)); | 
|  | return (wake_req > 0); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * External interrupt handler. | 
|  | */ | 
|  | void ottf_external_isr(void) { | 
|  | dif_pwrmgr_irq_t irq_id; | 
|  | top_earlgrey_plic_peripheral_t peripheral; | 
|  |  | 
|  | isr_testutils_pwrmgr_isr(plic_ctx, pwrmgr_isr_ctx, &peripheral, &irq_id); | 
|  |  | 
|  | // Check that both the peripheral and the irq id is correct | 
|  | CHECK(peripheral == kTopEarlgreyPlicPeripheralPwrmgrAon, | 
|  | "IRQ peripheral: %d is incorrect", peripheral); | 
|  | CHECK(irq_id == kDifPwrmgrIrqWakeup, "IRQ ID: %d is incorrect", irq_id); | 
|  | } | 
|  |  | 
|  | bool test_main(void) { | 
|  | dif_sensor_ctrl_t sensor_ctrl; | 
|  |  | 
|  | // Enable global and external IRQ at Ibex. | 
|  | irq_global_ctrl(true); | 
|  | irq_external_ctrl(true); | 
|  |  | 
|  | // Initialize the PLIC. | 
|  | mmio_region_t plic_base_addr = | 
|  | mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR); | 
|  | CHECK_DIF_OK(dif_rv_plic_init(plic_base_addr, &plic)); | 
|  |  | 
|  | // Initialize pwrmgr | 
|  | CHECK_DIF_OK(dif_pwrmgr_init( | 
|  | mmio_region_from_addr(TOP_EARLGREY_PWRMGR_AON_BASE_ADDR), &pwrmgr)); | 
|  |  | 
|  | // Initialize sensor_ctrl | 
|  | CHECK_DIF_OK(dif_sensor_ctrl_init( | 
|  | mmio_region_from_addr(TOP_EARLGREY_SENSOR_CTRL_BASE_ADDR), &sensor_ctrl)); | 
|  |  | 
|  | // Enable all the AON interrupts used in this test. | 
|  | rv_plic_testutils_irq_range_enable(&plic, kTopEarlgreyPlicTargetIbex0, | 
|  | kTopEarlgreyPlicIrqIdPwrmgrAonWakeup, | 
|  | kTopEarlgreyPlicIrqIdPwrmgrAonWakeup); | 
|  |  | 
|  | // Enable pwrmgr interrupt | 
|  | CHECK_DIF_OK(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled)); | 
|  |  | 
|  | // Capture the number of events to test | 
|  | uint32_t sensor_ctrl_events = SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS; | 
|  |  | 
|  | dif_pwrmgr_domain_config_t sleep_config = | 
|  | kDifPwrmgrDomainOptionMainPowerInLowPower; | 
|  |  | 
|  | for (size_t i = 0; i < sensor_ctrl_events; ++i) { | 
|  | LOG_INFO("Testing sensor_ctrl event %d", i); | 
|  |  | 
|  | // Setup event trigger | 
|  | CHECK_DIF_OK(dif_sensor_ctrl_set_ast_event_trigger(&sensor_ctrl, i, | 
|  | kDifToggleEnabled)); | 
|  |  | 
|  | // Normal sleep. | 
|  | pwrmgr_testutils_enable_low_power(&pwrmgr, kDifPwrmgrWakeupRequestSourceSix, | 
|  | sleep_config); | 
|  |  | 
|  | // Enter low power mode. | 
|  | LOG_INFO("Issue WFI to enter sleep"); | 
|  | wait_for_interrupt(); | 
|  |  | 
|  | // Wakeup from sleep. | 
|  | LOG_INFO("Wake from sleep"); | 
|  |  | 
|  | dif_sensor_ctrl_events_t events; | 
|  | CHECK_DIF_OK(dif_sensor_ctrl_get_recov_events(&sensor_ctrl, &events)); | 
|  |  | 
|  | if (events != (1 << i)) { | 
|  | LOG_ERROR("Recoverable event 0x%x does not match expectation %d", events, | 
|  | i); | 
|  | } | 
|  |  | 
|  | // clear event trigger | 
|  | CHECK_DIF_OK(dif_sensor_ctrl_set_ast_event_trigger(&sensor_ctrl, i, | 
|  | kDifToggleDisabled)); | 
|  |  | 
|  | // since there is synchronization delay to trigger clearing and actual event | 
|  | // de-assertion, clear and poll until it is finished before moving on | 
|  | while (events) { | 
|  | CHECK_DIF_OK(dif_sensor_ctrl_clear_recov_event(&sensor_ctrl, i)); | 
|  | CHECK_DIF_OK(dif_sensor_ctrl_get_recov_events(&sensor_ctrl, &events)); | 
|  | } | 
|  |  | 
|  | // ensure the de-asserted events have cleared from the wakeup pipeline | 
|  | // within 30us | 
|  | IBEX_SPIN_FOR(!get_wakeup_status(), 30); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } |