blob: b1d3d91c3cae9bd97d199828f068014a8ef29f62 [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_flash_ctrl.h"
#include "sw/device/lib/dif/dif_pwrmgr.h"
#include "sw/device/lib/dif/dif_rstmgr.h"
#include "sw/device/lib/dif/dif_rv_plic.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/flash_ctrl_testutils.h"
#include "sw/device/lib/testing/pwrmgr_testutils.h"
#include "sw/device/lib/testing/rand_testutils.h"
#include "sw/device/lib/testing/rstmgr_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 "sw/device/lib/testing/autogen/isr_testutils.h"
OTTF_DEFINE_TEST_CONFIG();
static dif_rv_plic_t plic;
static dif_aon_timer_t aon;
static plic_isr_ctx_t plic_ctx = {
.rv_plic = &plic,
.hart_id = kTopEarlgreyPlicTargetIbex0,
};
static aon_timer_isr_ctx_t aon_timer_ctx = {
.aon_timer = &aon,
.plic_aon_timer_start_irq_id =
kTopEarlgreyPlicIrqIdAonTimerAonWkupTimerExpired,
.is_only_irq = false,
};
static top_earlgrey_plic_peripheral_t peripheral_serviced;
static dif_aon_timer_irq_t irq_serviced;
enum {
kFlashDataRegion = 0,
kRegionBasePageIndex = 256, // First page in bank 1 (avoids program code.)
kPartitionId = 0,
kRegionSize = 1,
kNumWords = 128,
};
/**
* External interrupt handler.
*/
void ottf_external_isr(void) {
isr_testutils_aon_timer_isr(plic_ctx, aon_timer_ctx, &peripheral_serviced,
&irq_serviced);
}
static void enable_irqs(void) {
// Enable the AON bark interrupt.
CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
&plic, kTopEarlgreyPlicIrqIdAonTimerAonWdogTimerBark,
kTopEarlgreyPlicTargetIbex0, kDifToggleEnabled));
CHECK_DIF_OK(dif_rv_plic_irq_set_priority(
&plic, kTopEarlgreyPlicIrqIdAonTimerAonWdogTimerBark,
kDifRvPlicMaxPriority));
CHECK_DIF_OK(dif_rv_plic_target_set_threshold(
&plic, kTopEarlgreyPlicTargetIbex0, 0x0));
// Enable the external IRQ at Ibex.
irq_global_ctrl(true);
irq_external_ctrl(true);
}
bool test_main(void) {
dif_flash_ctrl_state_t flash;
dif_pwrmgr_t pwrmgr;
dif_rstmgr_t rstmgr;
CHECK_DIF_OK(dif_rv_plic_init(
mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR), &plic));
CHECK_DIF_OK(dif_flash_ctrl_init_state(
&flash, mmio_region_from_addr(TOP_EARLGREY_FLASH_CTRL_CORE_BASE_ADDR)));
CHECK_DIF_OK(dif_pwrmgr_init(
mmio_region_from_addr(TOP_EARLGREY_PWRMGR_AON_BASE_ADDR), &pwrmgr));
CHECK_DIF_OK(dif_rstmgr_init(
mmio_region_from_addr(TOP_EARLGREY_RSTMGR_AON_BASE_ADDR), &rstmgr));
CHECK_DIF_OK(dif_aon_timer_init(
mmio_region_from_addr(TOP_EARLGREY_AON_TIMER_AON_BASE_ADDR), &aon));
enable_irqs();
CHECK_DIF_OK(dif_aon_timer_watchdog_stop(&aon));
CHECK_DIF_OK(dif_pwrmgr_set_request_sources(&pwrmgr, kDifPwrmgrReqTypeReset,
kDifPwrmgrResetRequestSourceTwo,
kDifToggleEnabled));
dif_rstmgr_reset_info_bitfield_t rstmgr_reset_info;
rstmgr_reset_info = rstmgr_testutils_reason_get();
uint32_t address = flash_ctrl_testutils_data_region_setup(
&flash, kRegionBasePageIndex, kFlashDataRegion, kRegionSize);
if (rstmgr_reset_info == kDifRstmgrResetInfoPor) {
// Create data. Random data will be different than
// the 0xFFFFFFFF that is created with an erase.
uint32_t data[kNumWords];
for (int i = 0; i < kNumWords; ++i) {
data[i] = rand_testutils_gen32();
}
// Erasing the page and writing data to it followed
// by a read back and compare to sanity check basic operation.
CHECK(flash_ctrl_testutils_erase_and_write_page(
&flash, address, kPartitionId, data, kDifFlashCtrlPartitionTypeData,
kNumWords));
uint32_t readback_data[kNumWords];
CHECK(flash_ctrl_testutils_read(
&flash, address, kPartitionId, readback_data,
kDifFlashCtrlPartitionTypeData, kNumWords, 0));
CHECK_ARRAYS_EQ(data, readback_data, kNumWords);
// Setting up low power hint and starting watchdog timer followed by
// a flash operation (page erase) and WFI. This will create a bite
// interrupt at some time following the start of the flash operation.
pwrmgr_testutils_enable_low_power(&pwrmgr, kDifPwrmgrWakeupRequestSourceTwo,
0);
CHECK_DIF_OK(dif_aon_timer_watchdog_stop(&aon));
CHECK_DIF_OK(dif_aon_timer_watchdog_start(&aon, 0x40, 0x80, false, false));
dif_flash_ctrl_transaction_t transaction = {
.byte_address = address,
.op = kDifFlashCtrlOpPageErase,
.partition_type = kDifFlashCtrlPartitionTypeData,
.partition_id = kPartitionId,
.word_count = 0x0};
CHECK_DIF_OK(dif_flash_ctrl_start(&flash, transaction));
wait_for_interrupt();
// Return from interrupt. Stop the watchdog. Check the reset info
// is still POR and the interrupt came from the correct source.
// Check the erase operation completed successfully.
CHECK_DIF_OK(dif_aon_timer_watchdog_stop(&aon));
rstmgr_reset_info = rstmgr_testutils_reason_get();
CHECK(rstmgr_reset_info == kDifRstmgrResetInfoPor);
CHECK(peripheral_serviced == kTopEarlgreyPlicPeripheralAonTimerAon);
CHECK(irq_serviced == kDifAonTimerIrqWdogTimerBark);
CHECK(flash_ctrl_testutils_wait_transaction_end(&flash));
CHECK(flash_ctrl_testutils_read(
&flash, address, kPartitionId, readback_data,
kDifFlashCtrlPartitionTypeData, kNumWords, 0));
uint32_t expected_data[kNumWords];
memset(expected_data, 0xff, sizeof(expected_data));
CHECK_ARRAYS_EQ(readback_data, expected_data, kNumWords);
rstmgr_testutils_reason_clear();
} else {
LOG_ERROR("Unexepected reset type detected. Reset info = %08x",
rstmgr_reset_info);
return false;
}
return true;
}