| /* |
| * 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 "sw/device/lib/arch/device.h" |
| #include "sw/device/lib/base/memory.h" |
| #include "sw/device/lib/base/mmio.h" |
| #include "sw/device/lib/dif/dif_i2c.h" |
| #include "sw/device/lib/dif/dif_rstmgr.h" |
| #include "sw/device/lib/dif/dif_spi_device.h" |
| #include "sw/device/lib/dif/dif_spi_host.h" |
| #include "sw/device/lib/dif/dif_usbdev.h" |
| #include "sw/device/lib/dif/dif_ml_top.h" |
| #include "sw/device/lib/runtime/hart.h" |
| #include "sw/device/lib/runtime/log.h" |
| #include "sw/device/lib/testing/test_framework/check.h" |
| #include "sw/device/lib/testing/test_framework/ottf_main.h" |
| |
| #include "hw/top_matcha/sw/autogen/top_matcha.h" |
| #include "i2c_regs.h" |
| #include "spi_device_regs.h" |
| #include "spi_host_regs.h" |
| #include "usbdev_regs.h" |
| #include "ml_top_regs.h" |
| |
| OTTF_DEFINE_TEST_CONFIG(); |
| |
| /** |
| * RSTMGR SW_RST_CTRL Test |
| * |
| * This test checks RSTMGR.SW_RST_CTRL_N[index] with peripheral devices. |
| * There are 7 RSTMGR.SW_RST_CTRL_N registers and each register |
| * controls peripheral reset as follows: |
| * |
| * // index | device | test register | reset value | prgm value | |
| * // ------------------------------------------------------------------ |
| * // 0 | SPI_DEVICE | CFG | 0x7f00 | 0x3f0c |
| * // 1 | SPI_HOST0 | CONFIGOPTS | 0x0 | 0x3210000 |
| * // 2 | SPI_HOST1 | CONFIGOPTS | 0x0 | 0x6540000 |
| * // 3 | SPI_HOST2 | CONFIGOPTS | 0x0 | 0x4320000 |
| * // 4 | USB | EP_OUT_ENABLE | 0x0 | 0xc3 |
| * // 5 | USBAON | Not used |
| * // 6 | I2C0 | TIMING0 | 0x0 | 0x8b00cfe |
| * // 7 | I2C1 | TIMING1 | 0x0 | 0x114010d8 |
| * // 8 | I2C2 | TIMING2 | 0x0 | 0x19ec1595 |
| * // 9 | SMC | TODO(kunb) |
| * // 10 | ML_TOP | INIT_START | 0x0 | 0x1c1595 |
| * // 11 | CAM_I2C | TIMING3 | 0x0 | 0x12345678 |
| * // 12 | VIDEO | TODO(kunb) |
| * // 13 | AUDIO | TODO(kunb) |
| * |
| * 'test register' is a rw type register under each peripheral device. |
| * During the test, these registers are programmed with arbitrary values. ('prgm |
| * value') when sw resets are asserted, those values are updated to reset value. |
| * In each round, the rstmgr asserts reset to each peripheral device and |
| * each test register value is compared with the expected reset value. |
| */ |
| |
| #define MAKE_INIT_FUNC(dif_) \ |
| void dif_##_init(void *dif, uintptr_t base) { \ |
| CHECK_DIF_OK(dif##_##dif_##_init(mmio_region_from_addr(base), dif)); \ |
| } |
| |
| MAKE_INIT_FUNC(spi_device); |
| MAKE_INIT_FUNC(spi_host); |
| MAKE_INIT_FUNC(usbdev); |
| MAKE_INIT_FUNC(i2c); |
| MAKE_INIT_FUNC(ml_top); |
| |
| static void spi_device_config(void *dif) { |
| uintptr_t handle_address = |
| ((uintptr_t)dif - offsetof(dif_spi_device_handle_t, dev)); |
| dif_spi_device_handle_t *handle = (dif_spi_device_handle_t *)handle_address; |
| dif_spi_device_config_t cfg = { |
| .clock_polarity = kDifSpiDeviceEdgePositive, |
| .data_phase = kDifSpiDeviceEdgeNegative, |
| .tx_order = kDifSpiDeviceBitOrderLsbToMsb, |
| .rx_order = kDifSpiDeviceBitOrderLsbToMsb, |
| .device_mode = kDifSpiDeviceModeGeneric, |
| .mode_cfg = |
| { |
| .generic = |
| { |
| .rx_fifo_commit_wait = 63, |
| .rx_fifo_len = 0x800, |
| .tx_fifo_len = 0x800, |
| }, |
| }, |
| }; |
| CHECK_DIF_OK(dif_spi_device_configure(handle, cfg)); |
| } |
| |
| static void spi_host0_config(void *dif) { |
| dif_spi_host_config_t cfg = { |
| .spi_clock = 1000000, |
| .peripheral_clock_freq_hz = 10000000, |
| .chip_select = |
| { |
| .idle = 1, |
| .trail = 2, |
| .lead = 3, |
| }, |
| .full_cycle = false, |
| .cpha = false, |
| .cpol = false, |
| }; |
| CHECK_DIF_OK(dif_spi_host_configure(dif, cfg)); |
| } |
| |
| static void spi_host1_config(void *dif) { |
| dif_spi_host_config_t cfg = { |
| .spi_clock = 1000000, |
| .peripheral_clock_freq_hz = 10000000, |
| .chip_select = |
| { |
| .idle = 4, |
| .trail = 5, |
| .lead = 6, |
| }, |
| .full_cycle = false, |
| .cpha = false, |
| .cpol = false, |
| }; |
| CHECK_DIF_OK(dif_spi_host_configure(dif, cfg)); |
| } |
| |
| static void spi_host2_config(void *dif) { |
| dif_spi_host_config_t cfg = { |
| .spi_clock = 1000000, |
| .peripheral_clock_freq_hz = 10000000, |
| .chip_select = |
| { |
| .idle = 7, |
| .trail = 8, |
| .lead = 9, |
| }, |
| .full_cycle = false, |
| .cpha = false, |
| .cpol = false, |
| }; |
| CHECK_DIF_OK(dif_spi_host_configure(dif, cfg)); |
| } |
| |
| static void i2c0_config(void *dif) { |
| dif_i2c_config_t cfg = { |
| .scl_time_high_cycles = 3326, |
| .scl_time_low_cycles = 2224, |
| }; |
| CHECK_DIF_OK(dif_i2c_configure(dif, cfg)); |
| } |
| |
| static void i2c1_config(void *dif) { |
| dif_i2c_config_t cfg = { |
| .rise_cycles = 4312, |
| .fall_cycles = 4416, |
| }; |
| CHECK_DIF_OK(dif_i2c_configure(dif, cfg)); |
| } |
| |
| static void i2c2_config(void *dif) { |
| dif_i2c_config_t cfg = { |
| .start_signal_setup_cycles = 5525, |
| .start_signal_hold_cycles = 6636, |
| }; |
| CHECK_DIF_OK(dif_i2c_configure(dif, cfg)); |
| } |
| |
| static void ml_top_config(void *dif) { |
| } |
| |
| static void cam_i2c_config(void *dif) { |
| dif_i2c_config_t cfg = { |
| .start_signal_setup_cycles = 5525, |
| .start_signal_hold_cycles = 6636, |
| }; |
| CHECK_DIF_OK(dif_i2c_configure(dif, cfg)); |
| } |
| |
| static dif_spi_device_handle_t spi_dev; |
| static dif_spi_host_t spi_host0; |
| static dif_spi_host_t spi_host1; |
| static dif_spi_host_t spi_host2; |
| static dif_usbdev_t usbdev; |
| static dif_i2c_t i2c0; |
| static dif_i2c_t i2c1; |
| static dif_i2c_t i2c2; |
| static dif_ml_top_t ml_top; |
| static dif_i2c_t cam_i2c; |
| |
| typedef struct test { |
| /** |
| * Name of the peripheral. |
| */ |
| const char *name; |
| /** |
| * Base address for the peripheral. |
| */ |
| uintptr_t base; |
| /** |
| * Offset to the peripheral's test register. |
| */ |
| ptrdiff_t offset; |
| /** |
| * Handle to the DIF object for this peripheral. |
| */ |
| void *dif; |
| /** |
| * Initialization for the DIF based on an address location. |
| * |
| * May be `NULL`. |
| */ |
| void (*init)(void *dif, uintptr_t base); |
| /** |
| * Configuration and initialization actions for the device. This |
| * will be passed the value of `dif` above. |
| * |
| * If `NULL`, the test register will be set to 'reset_vals[]'. |
| */ |
| void (*config)(void *dif); |
| /** |
| * Arbitrary test register value before reset. |
| */ |
| uint32_t program_val; |
| /** |
| * The index in the reset manager for this device. |
| */ |
| uint32_t reset_index; |
| } test_t; |
| |
| static const test_t kPeripherals[] = { |
| { |
| .name = "SPI_DEVICE", |
| .base = TOP_MATCHA_SPI_DEVICE_BASE_ADDR, |
| .offset = SPI_DEVICE_CFG_REG_OFFSET, |
| .dif = &spi_dev.dev, |
| .init = spi_device_init, |
| .config = spi_device_config, |
| .program_val = 0x3f0c, |
| .reset_index = kTopMatchaResetManagerSwResetsSpiDevice, |
| }, |
| { |
| .name = "SPI_HOST0", |
| .base = TOP_MATCHA_SPI_HOST0_BASE_ADDR, |
| .offset = SPI_HOST_CONFIGOPTS_REG_OFFSET, |
| .dif = &spi_host0, |
| .init = spi_host_init, |
| .config = spi_host0_config, |
| .program_val = 0x3210000, |
| .reset_index = kTopMatchaResetManagerSwResetsSpiHost0, |
| }, |
| { |
| .name = "SPI_HOST1", |
| .base = TOP_MATCHA_SPI_HOST1_BASE_ADDR, |
| .offset = SPI_HOST_CONFIGOPTS_REG_OFFSET, |
| .dif = &spi_host1, |
| .init = spi_host_init, |
| .config = spi_host1_config, |
| .program_val = 0x6540000, |
| .reset_index = kTopMatchaResetManagerSwResetsSpiHost1, |
| }, |
| { |
| .name = "SPI_HOST2", |
| .base = TOP_MATCHA_SPI_HOST2_BASE_ADDR, |
| .offset = SPI_HOST_CONFIGOPTS_REG_OFFSET, |
| .dif = &spi_host2, |
| .init = spi_host_init, |
| .config = spi_host2_config, |
| .program_val = 0x4320000, |
| .reset_index = kTopMatchaResetManagerSwResetsSpiHost2, |
| }, |
| { |
| .name = "USB", |
| .base = TOP_MATCHA_USBDEV_BASE_ADDR, |
| .offset = USBDEV_EP_OUT_ENABLE_REG_OFFSET, |
| .dif = &usbdev, |
| .init = usbdev_init, |
| .program_val = 0xc3, |
| .reset_index = kTopMatchaResetManagerSwResetsUsb, |
| }, |
| { |
| .name = "I2C0", |
| .base = TOP_MATCHA_I2C0_BASE_ADDR, |
| .offset = I2C_TIMING0_REG_OFFSET, |
| .dif = &i2c0, |
| .init = i2c_init, |
| .config = i2c0_config, |
| .program_val = 0x8b00cfe, |
| .reset_index = kTopMatchaResetManagerSwResetsI2c0, |
| }, |
| { |
| .name = "I2C1", |
| .base = TOP_MATCHA_I2C1_BASE_ADDR, |
| .offset = I2C_TIMING1_REG_OFFSET, |
| .dif = &i2c1, |
| .init = i2c_init, |
| .config = i2c1_config, |
| .program_val = 0x114010d8, |
| .reset_index = kTopMatchaResetManagerSwResetsI2c1, |
| }, |
| { |
| .name = "I2C2", |
| .base = TOP_MATCHA_I2C2_BASE_ADDR, |
| .offset = I2C_TIMING2_REG_OFFSET, |
| .dif = &i2c2, |
| .init = i2c_init, |
| .config = i2c2_config, |
| .program_val = 0x19ec1595, |
| .reset_index = kTopMatchaResetManagerSwResetsI2c2, |
| }, |
| { |
| .name = "ML_TOP", |
| .base = TOP_MATCHA_ML_TOP_CORE_BASE_ADDR, |
| .offset = ML_TOP_INIT_START_REG_OFFSET, |
| .dif = &ml_top, |
| .init = ml_top_init, |
| .config = ml_top_config, |
| .program_val = 0x1c1595, |
| .reset_index = kTopMatchaResetManagerSwResetsMl, |
| }, |
| { |
| .name = "CAM_I2C", |
| .base = TOP_MATCHA_CAM_I2C_BASE_ADDR, |
| .offset = I2C_TIMING3_REG_OFFSET, |
| .dif = &cam_i2c, |
| .init = i2c_init, |
| .config = cam_i2c_config, |
| .program_val = 0x12345678, |
| .reset_index = kTopMatchaResetManagerSwResetsCamI2c, |
| } |
| }; |
| |
| /** |
| * Reads the value of the test register in a particular device. |
| */ |
| static uint32_t read_test_reg(const test_t *test) { |
| return mmio_region_read32(mmio_region_from_addr(test->base), test->offset); |
| } |
| |
| bool test_main(void) { |
| dif_rstmgr_t rstmgr; |
| CHECK_DIF_OK(dif_rstmgr_init( |
| mmio_region_from_addr(TOP_MATCHA_RSTMGR_AON_BASE_ADDR), &rstmgr)); |
| |
| uint32_t reset_vals[ARRAYSIZE(kPeripherals)]; |
| for (size_t i = 0; i < ARRAYSIZE(kPeripherals); ++i) { |
| if (kPeripherals[i].init != NULL) { |
| kPeripherals[i].init(kPeripherals[i].dif, kPeripherals[i].base); |
| } |
| reset_vals[i] = read_test_reg(&kPeripherals[i]); |
| LOG_INFO("reset_val for %s is 0x%08x", kPeripherals[i].name, reset_vals[i]); |
| } |
| |
| for (size_t i = 0; i < ARRAYSIZE(kPeripherals); ++i) { |
| if (kPeripherals[i].config != NULL) { |
| kPeripherals[i].config(kPeripherals[i].dif); |
| } else { |
| mmio_region_write32(mmio_region_from_addr(kPeripherals[i].base), |
| kPeripherals[i].offset, kPeripherals[i].program_val); |
| } |
| |
| // add read back to make sure |
| // all register access complete |
| uint32_t got = read_test_reg(&kPeripherals[i]); |
| LOG_INFO("programmed value : 0x%x", got); |
| } |
| |
| for (size_t i = 0; i < ARRAYSIZE(kPeripherals); ++i) { |
| CHECK_DIF_OK(dif_rstmgr_software_reset(&rstmgr, kPeripherals[i].reset_index, |
| kDifRstmgrSoftwareReset)); |
| |
| uint32_t got = read_test_reg(&kPeripherals[i]); |
| CHECK(got == reset_vals[i], |
| "after configure: reset_val for %s mismatch: want 0x%x, got 0x%x", |
| kPeripherals[i].name, reset_vals[i], got); |
| } |
| |
| return true; |
| } |