| // 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; |
| } |