From fea01b42540b2146d0160fb457e063e854fa322f Mon Sep 17 00:00:00 2001 From: Declan Snyder Date: Wed, 7 Sep 2022 19:40:20 -0500 Subject: [PATCH] tests: sd: Adds MMC test and Kconfigs Adds MMC test and Kconfigs for MMC Note: MMC not yet implemented as of this commit Signed-off-by: Declan Snyder --- subsys/sd/Kconfig | 15 +- tests/subsys/sd/mmc/CMakeLists.txt | 8 ++ tests/subsys/sd/mmc/README.txt | 29 ++++ tests/subsys/sd/mmc/prj.conf | 5 + tests/subsys/sd/mmc/src/main.c | 224 +++++++++++++++++++++++++++++ tests/subsys/sd/mmc/testcase.yaml | 10 ++ 6 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 tests/subsys/sd/mmc/CMakeLists.txt create mode 100644 tests/subsys/sd/mmc/README.txt create mode 100644 tests/subsys/sd/mmc/prj.conf create mode 100644 tests/subsys/sd/mmc/src/main.c create mode 100644 tests/subsys/sd/mmc/testcase.yaml diff --git a/subsys/sd/Kconfig b/subsys/sd/Kconfig index 891065cd995..37ef6372a77 100644 --- a/subsys/sd/Kconfig +++ b/subsys/sd/Kconfig @@ -5,6 +5,11 @@ menu "SD" +config MMC_STACK + bool "MMC protocol support" + help + Enable MMC protocol support. Required for eMMC cards to function. + config SDMMC_STACK bool "SDMMC protocol support" help @@ -18,7 +23,7 @@ config SDIO_STACK config SD_STACK bool - default y if SDMMC_STACK || SDIO_STACK + default y if MMC_STACK || SDMMC_STACK || SDIO_STACK select SDHC help Enable SD card support. @@ -65,6 +70,8 @@ config SD_BUFFER_SIZE # If SDHC required buffer alignment, we need a full block size in # internal buffer default 512 if SDHC_BUFFER_ALIGNMENT != 1 + # If MMC is being used, need 512 bytes to read EXT_CSD + default 512 if MMC_STACK # Otherwise, we only need 64 bytes to read SD switch function default 64 help @@ -85,6 +92,12 @@ config SD_UHS_PROTOCOL Enable support for ultra high speed SD cards. This can be disabled to reduce code size, at the cost of data transfer speeds. +config MMC_RCA + hex "MMC Relative card address" + default 2 + help + Relative card address to publish to MMC card. + endif # SD_STACK endmenu diff --git a/tests/subsys/sd/mmc/CMakeLists.txt b/tests/subsys/sd/mmc/CMakeLists.txt new file mode 100644 index 00000000000..39bc74be78b --- /dev/null +++ b/tests/subsys/sd/mmc/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(mmc_subsys_test) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/subsys/sd/mmc/README.txt b/tests/subsys/sd/mmc/README.txt new file mode 100644 index 00000000000..6b5c6b197c6 --- /dev/null +++ b/tests/subsys/sd/mmc/README.txt @@ -0,0 +1,29 @@ +MMC Subsystem Test +################## + +This test is designed to verify the MMC protocol stack implementation, +and run stress tests to verify large data transfers succeed using the +subsystem. Due to the differences between underlying SD host controller drivers, +this test also serves as a complete test for the SDHC driver implementation in +use. It requires an SD card be connected to the board to pass, and will +perform destructive I/O on the card, wiping any data present. The test has +the following phases: + +* Init test: verify the SD host controller can detect card presence, and + test the initialization flow of the MMC subsystem to verify that the stack + can correctly initialize an SD card. + +* IOCTL test: verify the SD subsystem correctly implements IOCTL calls required + for block devices in Zephyr. + +* Read test: verify that single block reads work, followed by multiple + block reads. Ensure the subsystem will reject reads beyond the end of + the card's stated size. + +* Write test: verify that single block writes work, followed by multiple + block writes. Ensure the subsystem will reject writes beyond the end of + the card's stated size. + +* R/W test: write data to the MMC card, and verify that it is able + to be read back without error. Perform this R/W combination at several + sector locations across the MMC card. diff --git a/tests/subsys/sd/mmc/prj.conf b/tests/subsys/sd/mmc/prj.conf new file mode 100644 index 00000000000..9f06d497b52 --- /dev/null +++ b/tests/subsys/sd/mmc/prj.conf @@ -0,0 +1,5 @@ +CONFIG_TEST=y +CONFIG_ZTEST=y +CONFIG_MMC_STACK=y +CONFIG_LOG=y +CONFIG_ZTEST_NEW_API=y diff --git a/tests/subsys/sd/mmc/src/main.c b/tests/subsys/sd/mmc/src/main.c new file mode 100644 index 00000000000..9efb5c453d9 --- /dev/null +++ b/tests/subsys/sd/mmc/src/main.c @@ -0,0 +1,224 @@ +/* + * Copyright 2022 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#define SECTOR_COUNT 32 +#define SECTOR_SIZE 512 /* subsystem should set all cards to 512 byte blocks */ +#define BUF_SIZE (SECTOR_SIZE * SECTOR_COUNT) +static const struct device *const sdhc_dev = DEVICE_DT_GET(DT_ALIAS(sdhc0)); +static struct sd_card card; +static uint8_t buf[BUF_SIZE] __aligned(CONFIG_SDHC_BUFFER_ALIGNMENT); +static uint8_t check_buf[BUF_SIZE] __aligned(CONFIG_SDHC_BUFFER_ALIGNMENT); +static uint32_t sector_size; +static uint32_t sector_count; + +#define MMC_UNALIGN_OFFSET 1 + +/* + * Verify that SD stack can initialize an MMC card + * This test must run first, to ensure the card is initialized. + */ +ZTEST(sd_stack, test_0_init) +{ + int ret; + + zassert_true(device_is_ready(sdhc_dev), "SDHC device is not ready"); + + ret = sd_init(sdhc_dev, &card); + + zassert_equal(ret, 0, "Card initialization failed"); +} + +/* Verify that MMC stack returns valid IOCTL values */ +ZTEST(sd_stack, test_ioctl) +{ + int ret; + + ret = mmc_ioctl(&card, DISK_IOCTL_GET_SECTOR_COUNT, §or_count); + zassert_equal(ret, 0, "IOCTL sector count read failed"); + TC_PRINT("SD card reports sector count of %d\n", sector_count); + + ret = mmc_ioctl(&card, DISK_IOCTL_GET_SECTOR_SIZE, §or_size); + zassert_equal(ret, 0, "IOCTL sector size read failed"); + TC_PRINT("SD card reports sector size of %d\n", sector_size); +} + +/* Verify that SD stack can read from an SD card */ +ZTEST(sd_stack, test_read) +{ + int ret; + int block_addr = 0; + + /* Try simple reads from start of SD card */ + + ret = mmc_read_blocks(&card, buf, block_addr, 1); + zassert_equal(ret, 0, "Single block card read failed"); + + ret = mmc_read_blocks(&card, buf, block_addr, SECTOR_COUNT / 2); + zassert_equal(ret, 0, "Multiple block card read failed"); + + /* Try a series of reads from the same block */ + block_addr = sector_count / 2; + for (int i = 0; i < 10; i++) { + ret = mmc_read_blocks(&card, buf, block_addr, SECTOR_COUNT); + zassert_equal(ret, 0, "Multiple reads from same addr failed"); + } + /* Verify that out of bounds read fails */ + block_addr = sector_count; + ret = mmc_read_blocks(&card, buf, block_addr, 1); + zassert_not_equal(ret, 0, "Out of bounds read should fail"); + + block_addr = sector_count - 2; + ret = mmc_read_blocks(&card, buf, block_addr, 2); + zassert_equal(ret, 0, "Read from end of card failed"); + + /* Verify that unaligned reads work */ + block_addr = 3; + ret = mmc_read_blocks(&card, buf + MMC_UNALIGN_OFFSET, block_addr, SECTOR_COUNT - 1); + zassert_equal(ret, 0, "Unaligned read failed"); +} + +/* Verify that SD stack can write to an SD card */ +ZTEST(sd_stack, test_write) +{ + int ret; + int block_addr = 0; + + /* Try simple writes from start of SD card */ + + ret = mmc_write_blocks(&card, buf, block_addr, 1); + zassert_equal(ret, 0, "Single block card write failed"); + + ret = mmc_write_blocks(&card, buf, block_addr, SECTOR_COUNT / 2); + zassert_equal(ret, 0, "Multiple block card write failed"); + + /* Try a series of reads from the same block */ + block_addr = sector_count / 2; + for (int i = 0; i < 10; i++) { + ret = mmc_write_blocks(&card, buf, block_addr, SECTOR_COUNT); + zassert_equal(ret, 0, "Multiple writes to same addr failed"); + } + /* Verify that out of bounds write fails */ + block_addr = sector_count; + ret = mmc_write_blocks(&card, buf, block_addr, 1); + zassert_not_equal(ret, 0, "Out of bounds write should fail"); + + block_addr = sector_count - 2; + ret = mmc_write_blocks(&card, buf, block_addr, 2); + zassert_equal(ret, 0, "Write to end of card failed"); + + /* Verify that unaligned writes work */ + block_addr = 3; + ret = mmc_write_blocks(&card, buf + MMC_UNALIGN_OFFSET, block_addr, SECTOR_COUNT - 1); + zassert_equal(ret, 0, "Unaligned write failed"); +} + +/* Test reads and writes interleaved, to verify data is making it on disk */ +ZTEST(sd_stack, test_rw) +{ + int ret; + int block_addr = 0; + + /* Zero the write buffer */ + memset(buf, 0, BUF_SIZE); + memset(check_buf, 0, BUF_SIZE); + ret = mmc_write_blocks(&card, buf, block_addr, SECTOR_COUNT / 2); + zassert_equal(ret, 0, "Write to card failed"); + /* Verify that a read from this area is empty */ + ret = mmc_read_blocks(&card, buf, block_addr, SECTOR_COUNT / 2); + zassert_equal(ret, 0, "Read from card failed"); + zassert_mem_equal(buf, check_buf, BUF_SIZE, "Read of erased area was not zero"); + + /* Now write nonzero data block */ + for (int i = 0; i < sizeof(buf); i++) { + check_buf[i] = buf[i] = (uint8_t)i; + } + + ret = mmc_write_blocks(&card, buf, block_addr, SECTOR_COUNT); + zassert_equal(ret, 0, "Write to card failed"); + /* Clear the read buffer, then write to it again */ + memset(buf, 0, BUF_SIZE); + ret = mmc_read_blocks(&card, buf, block_addr, SECTOR_COUNT); + zassert_equal(ret, 0, "Read from card failed"); + zassert_mem_equal(buf, check_buf, BUF_SIZE, "Read of written area was not correct"); + + block_addr = (sector_count / 3); + for (int i = 0; i < 10; i++) { + /* Verify that unaligned writes work */ + ret = mmc_write_blocks(&card, buf + MMC_UNALIGN_OFFSET, block_addr, + SECTOR_COUNT - 1); + zassert_equal(ret, 0, "Write to card failed"); + /* Zero check buffer and read into it */ + memset(check_buf + MMC_UNALIGN_OFFSET, 0, (SECTOR_COUNT - 1) * sector_size); + ret = mmc_read_blocks(&card, check_buf + MMC_UNALIGN_OFFSET, block_addr, + (SECTOR_COUNT - 1)); + zassert_equal(ret, 0, "Read from card failed"); + zassert_mem_equal(buf + MMC_UNALIGN_OFFSET, check_buf + MMC_UNALIGN_OFFSET, + (SECTOR_COUNT - 1) * sector_size, + "Unaligned read of written area was not correct"); + } +} + +/* Simply dump the card configuration. */ +ZTEST(sd_stack, test_card_config) +{ + switch (card.card_voltage) { + case SD_VOL_1_2_V: + TC_PRINT("Card voltage: 1.2V\n"); + break; + case SD_VOL_1_8_V: + TC_PRINT("Card voltage: 1.8V\n"); + break; + case SD_VOL_3_0_V: + TC_PRINT("Card voltage: 3.0V\n"); + break; + case SD_VOL_3_3_V: + TC_PRINT("Card voltage: 3.3V\n"); + break; + default: + zassert_unreachable("Card voltage is not known value"); + } + zassert_equal(card.status, CARD_INITIALIZED, "Card status is not OK"); + switch (card.card_speed) { + case MMC_LEGACY_TIMING: + TC_PRINT("Card timing: Legacy MMC\n"); + break; + case MMC_HS_TIMING: + TC_PRINT("Card timing: High Speed MMC\n"); + break; + case MMC_HS200_TIMING: + TC_PRINT("Card timing: MMC HS200\n"); + break; + case MMC_HS400_TIMING: + TC_PRINT("Card timing: MMC HS400\n"); + break; + default: + zassert_unreachable("Card timing is not known value"); + } + switch (card.type) { + case CARD_SDIO: + TC_PRINT("Card type: SDIO\n"); + break; + case CARD_SDMMC: + TC_PRINT("Card type: SDMMC\n"); + break; + case CARD_COMBO: + TC_PRINT("Card type: combo card\n"); + break; + case CARD_MMC: + TC_PRINT("Card type: MMC\n"); + break; + default: + zassert_unreachable("Card type is not known value"); + } +} + +ZTEST_SUITE(sd_stack, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/subsys/sd/mmc/testcase.yaml b/tests/subsys/sd/mmc/testcase.yaml new file mode 100644 index 00000000000..44e79c4bc56 --- /dev/null +++ b/tests/subsys/sd/mmc/testcase.yaml @@ -0,0 +1,10 @@ +common: + depends_on: sdhc + tags: drivers sdhc +tests: + subsys.sd.mmc: + harness: ztest + filter: dt_compat_enabled("zephyr,mmc-disk") + tags: sdhc + min_ram: 32 + integration_platforms: