blob: b48adb8bc51ce434c6ef22ccf109bd6f8e9ae37a [file] [log] [blame]
/*
* Copyright 2023 Google LLC
* Copyright lowRISC contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "dma_regs.h"
#include "hw/top_matcha/sw/autogen/top_matcha.h"
#include "ml_top_regs.h"
#include "spi_host_regs.h"
#include "sw/device/lib/base/abs_mmio.h"
#include "sw/device/lib/base/memory.h"
#include "sw/device/lib/dif/dif_aon_timer.h"
#include "sw/device/lib/dif/dif_base.h"
#include "sw/device/lib/dif/dif_clkmgr.h"
#include "sw/device/lib/dif/dif_dma.h"
#include "sw/device/lib/dif/dif_flash_ctrl.h"
#include "sw/device/lib/dif/dif_ml_top.h"
#include "sw/device/lib/dif/dif_pwrmgr.h"
#include "sw/device/lib/dif/dif_rstmgr.h"
#include "sw/device/lib/dif/dif_spi_host.h"
#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/dif/dif_usbdev.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/aon_timer_testutils.h"
#include "sw/device/lib/testing/flash_ctrl_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 "uart_regs.h"
#include "usbdev_regs.h"
/**
* The peripherals used to test when the peri clocks are disabled are
* bit 0: clk_io_div4_peri: uart0
* bit 1: clk_io_div2_peri: spi_host1
* bit 2: clk_io_peri: spi_host0
* bit 3: clk_usb_peri: usbdev
* bit 4: clk_video:
* bit 5: clk_ml: ml_top
* bit 6: clk_audio:
* bit 7: clk_smc: dma_smc
*/
OTTF_DEFINE_TEST_CONFIG();
typedef struct peri_context {
void (*csr_access)(); // The function causing a timeout.
uint32_t address; // The address causing a timeout.
} peri_context_t;
static dif_aon_timer_t aon_timer;
static dif_flash_ctrl_state_t flash_ctrl;
static dif_spi_host_t spi_host0;
static dif_spi_host_t spi_host1;
static dif_usbdev_t usbdev;
static dif_uart_t uart0;
static dif_ml_top_t ml_top;
static dif_dma_t dma_smc;
OT_SECTION(".non_volatile_scratch") uint64_t hung_data_addr[4];
static void set_hung_address(dif_clkmgr_gateable_clock_t clock,
uint32_t value) {
uint32_t addr =
(uintptr_t)&hung_data_addr[clock] - TOP_MATCHA_FLASH_CTRL_MEM_BASE_ADDR;
uint32_t flash_word[2] = {value, 0};
CHECK(flash_ctrl_testutils_write(&flash_ctrl, addr, 0, flash_word,
kDifFlashCtrlPartitionTypeData, 2));
CHECK(hung_data_addr[clock] == value, "Unexpected mismatch on read back");
LOG_INFO("The expected hung address for clock %d is 0x%x at 0x%x", clock,
value, addr);
}
static void uart0_csr_access() {
dif_toggle_t state;
CHECK_DIF_OK(dif_uart_irq_set_enabled(&uart0, kDifUartIrqTxWatermark,
kDifToggleEnabled));
CHECK_DIF_OK(
dif_uart_irq_get_enabled(&uart0, kDifUartIrqTxWatermark, &state));
CHECK(state == kDifToggleEnabled);
}
static void spi_host0_csr_access() {
dif_toggle_t state;
CHECK_DIF_OK(dif_spi_host_irq_set_enabled(&spi_host0, kDifSpiHostIrqSpiEvent,
kDifToggleEnabled));
CHECK_DIF_OK(
dif_spi_host_irq_get_enabled(&spi_host0, kDifSpiHostIrqSpiEvent, &state));
CHECK(state == kDifToggleEnabled);
}
static void spi_host1_csr_access() {
dif_toggle_t state;
CHECK_DIF_OK(dif_spi_host_irq_set_enabled(&spi_host1, kDifSpiHostIrqSpiEvent,
kDifToggleEnabled));
CHECK_DIF_OK(
dif_spi_host_irq_get_enabled(&spi_host1, kDifSpiHostIrqSpiEvent, &state));
CHECK(state == kDifToggleEnabled);
}
static void usbdev_csr_access() {
CHECK_DIF_OK(dif_usbdev_irq_set_enabled(&usbdev, kDifUsbdevIrqPowered,
kDifToggleEnabled));
dif_toggle_t state;
CHECK_DIF_OK(
dif_usbdev_irq_get_enabled(&usbdev, kDifUsbdevIrqPowered, &state));
CHECK(state == kDifToggleEnabled);
}
static void ml_top_csr_access() {
CHECK_DIF_OK(dif_ml_top_irq_set_enabled(&ml_top, kDifMlTopIrqHostReq,
kDifToggleEnabled));
dif_toggle_t state;
CHECK_DIF_OK(
dif_ml_top_irq_get_enabled(&ml_top, kDifMlTopIrqHostReq, &state));
CHECK(state == kDifToggleEnabled);
}
static void dma_smc_csr_access() {
CHECK_DIF_OK(dif_dma_irq_set_enabled(&dma_smc, kDifDmaIrqWriterDone,
kDifToggleEnabled));
dif_toggle_t state;
CHECK_DIF_OK(dif_dma_irq_get_enabled(&dma_smc, kDifDmaIrqWriterDone, &state));
CHECK(state == kDifToggleEnabled);
}
peri_context_t peri_context[kTopMatchaGateableClocksLast + 1] = {
{uart0_csr_access,
TOP_MATCHA_UART0_BASE_ADDR + UART_INTR_ENABLE_REG_OFFSET},
{spi_host1_csr_access,
TOP_MATCHA_SPI_HOST1_BASE_ADDR + SPI_HOST_INTR_ENABLE_REG_OFFSET},
{spi_host0_csr_access,
TOP_MATCHA_SPI_HOST0_BASE_ADDR + SPI_HOST_INTR_ENABLE_REG_OFFSET},
{usbdev_csr_access,
TOP_MATCHA_USBDEV_BASE_ADDR + USBDEV_INTR_ENABLE_REG_OFFSET},
{},
{ml_top_csr_access,
TOP_MATCHA_ML_TOP_CORE_BASE_ADDR + ML_TOP_INTR_ENABLE_REG_OFFSET},
{},
{dma_smc_csr_access,
TOP_MATCHA_DMA_SMC_BASE_ADDR + DMA_INTR_ENABLE_REG_OFFSET}};
/**
* Test that disabling a 'gateable' unit's clock causes the unit to become
* unresponsive to CSR accesses. Configure a watchdog reset, and if it triggers
* the test is successful.
*/
static void test_gateable_clocks_off(const dif_clkmgr_t *clkmgr,
const dif_pwrmgr_t *pwrmgr,
dif_clkmgr_gateable_clock_t clock) {
// Make sure the clock for the unit is on.
CHECK_DIF_OK(
dif_clkmgr_gateable_clock_set_enabled(clkmgr, clock, kDifToggleEnabled));
// Enable watchdog bite reset.
CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset,
kDifPwrmgrResetRequestSourceTwo,
kDifToggleEnabled));
LOG_INFO("Testing peripheral clock %d", clock);
// Bite after enough time has elapsed past the hung csr access.
uint32_t bite_us = (kDeviceType == kDeviceSimDV) ? 400 : 800;
uint32_t bite_cycles = aon_timer_testutils_get_aon_cycles_from_us(bite_us);
LOG_INFO("Setting bite reset for %u us (%u cycles)", bite_us, bite_cycles);
// Make sure the CSR is accessible before turning the clock off.
(*peri_context[clock].csr_access)();
LOG_INFO("CSR access was okay before disabling the clock");
// Save the expected hung address to check against cpu_info's LAST_DATA_ADDR.
set_hung_address(clock, peri_context[clock].address);
// Set bite timer.
aon_timer_testutils_watchdog_config(&aon_timer, UINT32_MAX, bite_cycles,
false);
// Disable the peripheral's clock.
CHECK_DIF_OK(
dif_clkmgr_gateable_clock_set_enabled(clkmgr, clock, kDifToggleDisabled));
// Wait for the clock to really turn off.
busy_spin_micros(100);
// And issue the CSR access that will freeze and cause a reset.
(*peri_context[clock].csr_access)();
}
/**
* Turn off the clock for 400us, then turn on
* Check the result in chip_sw_clkmgr_off_peri_vseq
*/
static void test_gateable_clocks_off_without_reset(
const dif_clkmgr_t *clkmgr, dif_clkmgr_gateable_clock_t clock) {
// Make sure the clock for the unit is on.
CHECK_DIF_OK(
dif_clkmgr_gateable_clock_set_enabled(clkmgr, clock, kDifToggleEnabled));
LOG_INFO("Testing peripheral clock %d", clock);
// Disable the peripheral's clock.
CHECK_DIF_OK(
dif_clkmgr_gateable_clock_set_enabled(clkmgr, clock, kDifToggleDisabled));
// Wait for the clock to really turn off.
busy_spin_micros(400);
// Enalbe the clock
CHECK_DIF_OK(
dif_clkmgr_gateable_clock_set_enabled(clkmgr, clock, kDifToggleEnabled));
}
bool test_main(void) {
dif_clkmgr_t clkmgr;
dif_pwrmgr_t pwrmgr;
dif_rstmgr_t rstmgr;
CHECK_DIF_OK(dif_rstmgr_init(
mmio_region_from_addr(TOP_MATCHA_RSTMGR_AON_BASE_ADDR), &rstmgr));
CHECK_DIF_OK(dif_clkmgr_init(
mmio_region_from_addr(TOP_MATCHA_CLKMGR_AON_BASE_ADDR), &clkmgr));
CHECK_DIF_OK(dif_pwrmgr_init(
mmio_region_from_addr(TOP_MATCHA_PWRMGR_AON_BASE_ADDR), &pwrmgr));
CHECK_DIF_OK(dif_flash_ctrl_init_state(
&flash_ctrl,
mmio_region_from_addr(TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR)));
// Initialize aon timer.
CHECK_DIF_OK(dif_aon_timer_init(
mmio_region_from_addr(TOP_MATCHA_AON_TIMER_AON_BASE_ADDR), &aon_timer));
// Initialize peripherals.
CHECK_DIF_OK(
dif_uart_init(mmio_region_from_addr(TOP_MATCHA_UART0_BASE_ADDR), &uart0));
CHECK_DIF_OK(dif_spi_host_init(
mmio_region_from_addr(TOP_MATCHA_SPI_HOST0_BASE_ADDR), &spi_host0));
CHECK_DIF_OK(dif_spi_host_init(
mmio_region_from_addr(TOP_MATCHA_SPI_HOST1_BASE_ADDR), &spi_host1));
CHECK_DIF_OK(dif_usbdev_init(
mmio_region_from_addr(TOP_MATCHA_USBDEV_BASE_ADDR), &usbdev));
CHECK_DIF_OK(dif_ml_top_init(
mmio_region_from_addr(TOP_MATCHA_ML_TOP_CORE_BASE_ADDR), &ml_top));
CHECK_DIF_OK(dif_dma_init(mmio_region_from_addr(TOP_MATCHA_DMA_SMC_BASE_ADDR),
&dma_smc));
// Enable cpu dump capture.
CHECK_DIF_OK(dif_rstmgr_cpu_info_set_enabled(&rstmgr, kDifToggleEnabled));
// Enable raw flash access.
flash_ctrl_testutils_default_region_access(&flash_ctrl,
/*rd_en*/ true,
/*prog_en*/ true,
/*erase_en*/ true,
/*scramble_en*/ false,
/*ecc_en*/ false,
/*he_en*/ false);
if (rstmgr_testutils_is_reset_info(&rstmgr, kDifRstmgrResetInfoPor)) {
rstmgr_testutils_pre_reset(&rstmgr);
// Starting clock.
dif_clkmgr_gateable_clock_t clock = kTopMatchaGateableClocksIoDiv4Peri;
LOG_INFO("Next clock to test %d", flash_ctrl_testutils_counter_get(0));
test_gateable_clocks_off(&clkmgr, &pwrmgr, clock);
// This should never be reached.
LOG_ERROR("This is unreachable since a reset should have been triggered");
return false;
} else if (rstmgr_testutils_is_reset_info(&rstmgr,
kDifRstmgrResetInfoWatchdog)) {
dif_clkmgr_gateable_clock_t clock = flash_ctrl_testutils_counter_get(0);
LOG_INFO("Got an expected watchdog reset when reading for clock %d", clock);
size_t actual_size;
CHECK_DIF_OK(dif_rstmgr_cpu_info_get_size(&rstmgr, &actual_size));
// Verify the cpu crash dump.
dif_rstmgr_cpu_info_dump_segment_t cpu_dump[DIF_RSTMGR_CPU_INFO_MAX_SIZE];
size_t size_read;
CHECK_DIF_OK(dif_rstmgr_cpu_info_dump_read(
&rstmgr, cpu_dump, DIF_RSTMGR_CPU_INFO_MAX_SIZE, &size_read));
CHECK(size_read <= DIF_RSTMGR_CPU_INFO_MAX_SIZE);
CHECK(size_read == actual_size);
LOG_INFO("EXC_ADDR = 0x%x", cpu_dump[0]);
LOG_INFO("EXC_PC = 0x%x", cpu_dump[1]);
LOG_INFO("LAST_DATA ADDR = 0x%x", cpu_dump[2]);
LOG_INFO("NEXT_PC = 0x%x", cpu_dump[3]);
LOG_INFO("CURRENT_PC = 0x%x", cpu_dump[4]);
LOG_INFO("PREV_EXC_ADDR = 0x%x", cpu_dump[5]);
LOG_INFO("PREV_EXC_PC = 0x%x", cpu_dump[6]);
LOG_INFO("PREV_VALID = 0x%x", cpu_dump[7]);
uint32_t expected_hung_address = hung_data_addr[clock];
LOG_INFO("The expected hung address = 0x%x", expected_hung_address);
CHECK(cpu_dump[2] == expected_hung_address, "Unexpected hung address");
// Mark this clock as tested.
flash_ctrl_testutils_counter_increment(&flash_ctrl, 0);
if (clock < kTopMatchaGateableClocksLast) {
clock = flash_ctrl_testutils_counter_get(0);
LOG_INFO("Next clock to test %d", clock);
if (clock == kTopMatchaGateableClocksIoDiv2Peri ||
clock == kTopMatchaGateableClocksVideoPeri ||
clock == kTopMatchaGateableClocksAudioPeri) {
test_gateable_clocks_off_without_reset(&clkmgr, clock);
flash_ctrl_testutils_counter_increment(&flash_ctrl, 0);
clock = flash_ctrl_testutils_counter_get(0);
if (clock == kTopMatchaGateableClocksLast) {
test_gateable_clocks_off_without_reset(&clkmgr, clock);
return true;
}
}
rstmgr_testutils_pre_reset(&rstmgr);
test_gateable_clocks_off(&clkmgr, &pwrmgr, clock);
// This should never be reached.
LOG_ERROR("This is unreachable since a reset should have been triggered");
return false;
} else {
return true;
}
} else {
dif_rstmgr_reset_info_bitfield_t reset_info;
reset_info = rstmgr_testutils_reason_get();
LOG_ERROR("Unexpected reset_info 0x%x", reset_info);
}
return false;
}