| // 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/testing/json/spi_passthru.h" |
| |
| #include <stdbool.h> |
| #include <stdint.h> |
| |
| #include "sw/device/lib/base/status.h" |
| #include "sw/device/lib/dif/dif_spi_device.h" |
| #include "sw/device/lib/runtime/log.h" |
| #include "sw/device/lib/testing/json/command.h" |
| #include "sw/device/lib/testing/spi_device_testutils.h" |
| #include "sw/device/lib/testing/test_framework/check.h" |
| #include "sw/device/lib/testing/test_framework/ottf_flow_control.h" |
| #include "sw/device/lib/testing/test_framework/ottf_main.h" |
| #include "sw/device/lib/testing/test_framework/ujson_ottf.h" |
| #include "sw/device/lib/ujson/ujson.h" |
| |
| #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" |
| |
| OTTF_DEFINE_TEST_CONFIG(.enable_uart_flow_control = true); |
| |
| static dif_spi_device_handle_t spid; |
| |
| static status_t configure_jedec_id(ujson_t *uj, dif_spi_device_handle_t *spid) { |
| config_jedec_id_t config; |
| TRY(ujson_deserialize_config_jedec_id_t(uj, &config)); |
| dif_spi_device_flash_id_t id = { |
| .device_id = config.device_id, |
| .manufacturer_id = config.manufacturer_id, |
| .continuation_code = config.continuation_code, |
| .num_continuation_code = config.continuation_len, |
| }; |
| TRY(dif_spi_device_set_flash_id(spid, id)); |
| return RESP_OK_STATUS(uj); |
| } |
| |
| static status_t write_status_register(ujson_t *uj, |
| dif_spi_device_handle_t *spid) { |
| status_register_t sr; |
| TRY(ujson_deserialize_status_register_t(uj, &sr)); |
| TRY(dif_spi_device_set_flash_status_registers(spid, sr.status)); |
| return RESP_OK_STATUS(uj); |
| } |
| |
| static status_t read_status_register(ujson_t *uj, |
| dif_spi_device_handle_t *spid) { |
| status_register_t sr; |
| dif_toggle_t addr_4b; |
| TRY(dif_spi_device_get_flash_status_registers(spid, &sr.status)); |
| TRY(dif_spi_device_get_4b_address_mode(spid, &addr_4b)); |
| sr.addr_4b = addr_4b; |
| RESP_OK(ujson_serialize_status_register_t, uj, &sr); |
| return OK_STATUS(); |
| } |
| |
| static status_t write_sfdp_data(ujson_t *uj, dif_spi_device_handle_t *spid) { |
| sfdp_data_t sfdp; |
| TRY(ujson_deserialize_sfdp_data_t(uj, &sfdp)); |
| TRY(dif_spi_device_write_flash_buffer(spid, kDifSpiDeviceFlashBufferTypeSfdp, |
| 0, sizeof(sfdp.data), sfdp.data)); |
| return RESP_OK_STATUS(uj); |
| } |
| |
| status_t command_processor(ujson_t *uj) { |
| while (true) { |
| test_command_t command; |
| TRY(ujson_deserialize_test_command_t(uj, &command)); |
| switch (command) { |
| case kTestCommandSpiConfigureJedecId: |
| RESP_ERR(uj, configure_jedec_id(uj, &spid)); |
| break; |
| case kTestCommandSpiReadStatus: |
| RESP_ERR(uj, read_status_register(uj, &spid)); |
| break; |
| case kTestCommandSpiWriteStatus: |
| RESP_ERR(uj, write_status_register(uj, &spid)); |
| break; |
| case kTestCommandSpiWriteSfdp: |
| RESP_ERR(uj, write_sfdp_data(uj, &spid)); |
| break; |
| |
| default: |
| LOG_ERROR("Unrecognized command: %d", command); |
| RESP_ERR(uj, INVALID_ARGUMENT()); |
| } |
| } |
| // We should never reach here. |
| return INTERNAL(); |
| } |
| |
| bool test_main(void) { |
| CHECK_DIF_OK(dif_spi_device_init_handle( |
| mmio_region_from_addr(TOP_EARLGREY_SPI_DEVICE_BASE_ADDR), &spid)); |
| |
| // We want to block passthru of the first 5 read commands, corresponding to |
| // ReadStatus{1,2,3}, ReadJedecID and ReadSfdp. |
| // We also block all write commands. |
| spi_device_testutils_configure_passthrough(&spid, |
| /*filters=*/0x1F, |
| /*upload_write_commands=*/true); |
| |
| dif_spi_device_passthrough_intercept_config_t passthru_cfg = { |
| .status = true, |
| .jedec_id = true, |
| .sfdp = true, |
| .mailbox = false, |
| }; |
| CHECK_DIF_OK( |
| dif_spi_device_set_passthrough_intercept_config(&spid, passthru_cfg)); |
| |
| ujson_t uj = ujson_ottf_console(); |
| status_t s = command_processor(&uj); |
| LOG_INFO("status = %r", s); |
| return status_ok(s); |
| } |