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