blob: 9baa3211b8f0a8976276a76d39b09971d61f4d17 [file] [log] [blame]
// 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_aon_timer.h"
#include "sw/device/lib/dif/dif_pwrmgr.h"
#include "sw/device/lib/dif/dif_rv_plic.h"
#include "sw/device/lib/runtime/irq.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/aon_timer_testutils.h"
#include "sw/device/lib/testing/pwrmgr_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"
OTTF_DEFINE_TEST_CONFIG();
static const uint32_t kPlicTarget = kTopEarlgreyPlicTargetIbex0;
static dif_aon_timer_t aon_timer;
static dif_rv_plic_t plic;
// Volatile globals accessed from the ISR.
static volatile dif_aon_timer_irq_t irq;
static volatile top_earlgrey_plic_peripheral_t peripheral;
static volatile bool interrupt_serviced;
static volatile bool interrupt_failed;
bool is_pwrmgr_irq_pending(void) {
bool status;
CHECK_DIF_OK(dif_rv_plic_irq_is_pending(
&plic, kTopEarlgreyPlicIrqIdPwrmgrAonWakeup, &status));
return status;
}
/**
* External interrupt handler.
*/
void ottf_external_isr(void) {
dif_rv_plic_irq_id_t irq_id;
if (is_pwrmgr_irq_pending()) {
interrupt_failed = true;
return;
}
CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic, kPlicTarget, &irq_id));
if (irq_id != kTopEarlgreyPlicIrqIdAonTimerAonWkupTimerExpired) {
interrupt_failed = true;
return;
}
peripheral = (top_earlgrey_plic_peripheral_t)
top_earlgrey_plic_interrupt_for_peripheral[irq_id];
if (peripheral == kTopEarlgreyPlicPeripheralAonTimerAon) {
irq =
(dif_aon_timer_irq_t)(irq_id -
(dif_rv_plic_irq_id_t)
kTopEarlgreyPlicIrqIdAonTimerAonWkupTimerExpired);
CHECK_DIF_OK(dif_aon_timer_wakeup_stop(&aon_timer));
CHECK_DIF_OK(dif_aon_timer_irq_acknowledge(&aon_timer, irq));
} else {
interrupt_failed = true;
return;
}
// Complete the IRQ by writing the IRQ source to the Ibex specific CC.
// register
CHECK_DIF_OK(dif_rv_plic_irq_complete(&plic, kPlicTarget, irq_id));
interrupt_serviced = true;
interrupt_failed = false;
}
bool test_main(void) {
dif_pwrmgr_t pwrmgr;
// Issue a wakeup signal in 200us through the AON timer.
//
// At 200kHz, threshold of 40 is equal to 200us.
//
// Adjust the threshold for Verilator since it runs on different clock
// frequencies.
uint32_t wakeup_threshold = 40;
if (kDeviceType == kDeviceSimVerilator) {
wakeup_threshold *= 10;
}
interrupt_serviced = false;
interrupt_failed = true;
// Initialize unit difs.
CHECK_DIF_OK(dif_pwrmgr_init(
mmio_region_from_addr(TOP_EARLGREY_PWRMGR_AON_BASE_ADDR), &pwrmgr));
CHECK_DIF_OK(dif_aon_timer_init(
mmio_region_from_addr(TOP_EARLGREY_AON_TIMER_AON_BASE_ADDR), &aon_timer));
CHECK_DIF_OK(dif_rv_plic_init(
mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR), &plic));
// Notice we are clearing rstmgr's RESET_INFO, so after the aon wakeup there
// is only one bit set.
if (pwrmgr_testutils_is_wakeup_reason(&pwrmgr, 0)) {
LOG_INFO("POR reset");
aon_timer_testutils_wakeup_config(&aon_timer, wakeup_threshold);
// Enable aon wakeup.
CHECK_DIF_OK(dif_pwrmgr_set_request_sources(
&pwrmgr, kDifPwrmgrReqTypeWakeup, kDifPwrmgrWakeupRequestSourceFive,
kDifToggleEnabled));
LOG_INFO("Enabled aon wakeup");
// Enable the AON wakeup interrupt.
CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
&plic, kTopEarlgreyPlicIrqIdAonTimerAonWkupTimerExpired, kPlicTarget,
kDifToggleEnabled));
LOG_INFO("Enabled aon wakeup interrupt");
CHECK_DIF_OK(dif_rv_plic_irq_set_priority(
&plic, kTopEarlgreyPlicIrqIdAonTimerAonWkupTimerExpired, 3));
LOG_INFO("Set aon wakeup interrupt priority");
// Enable pwrmgr wakeup interrupt, so it triggers an interrupt even though
// it should not.
CHECK_DIF_OK(dif_pwrmgr_irq_set_enabled(&pwrmgr, kDifPwrmgrIrqWakeup,
kDifToggleEnabled));
CHECK_DIF_OK(
dif_rv_plic_irq_set_enabled(&plic, kTopEarlgreyPlicIrqIdPwrmgrAonWakeup,
kPlicTarget, kDifToggleEnabled));
LOG_INFO("Enabled pwrmgr wakeup interrupt");
CHECK_DIF_OK(dif_rv_plic_irq_set_priority(
&plic, kTopEarlgreyPlicIrqIdPwrmgrAonWakeup, 3));
LOG_INFO("Set pwrmgr wakeup interrupt priority");
// Prepare for interrupt.
LOG_INFO("Issue WFI without sleep");
irq_global_ctrl(true);
irq_external_ctrl(true);
wait_for_interrupt();
// Check that interrupt was serviced correctly.
CHECK(interrupt_serviced);
CHECK(!interrupt_failed);
LOG_INFO("The interrupt was processed");
// And to be extra safe, check there is no pwrmgr interrupt pending.
CHECK(!is_pwrmgr_irq_pending());
return true;
} else if (pwrmgr_testutils_is_wakeup_reason(
&pwrmgr, kDifPwrmgrWakeupRequestSourceFive)) {
LOG_ERROR("Unexpected wakeup reset");
return false;
}
return false;
}