14c579e15SShengtan Mao /* 24c579e15SShengtan Mao * QTests for NPCM7xx SD-3.0 / MMC-4.51 Host Controller 34c579e15SShengtan Mao * 44c579e15SShengtan Mao * Copyright (c) 2022 Google LLC 54c579e15SShengtan Mao * 64c579e15SShengtan Mao * This program is free software; you can redistribute it and/or modify it 74c579e15SShengtan Mao * under the terms of the GNU General Public License as published by the 84c579e15SShengtan Mao * Free Software Foundation; either version 2 of the License, or 94c579e15SShengtan Mao * (at your option) any later version. 104c579e15SShengtan Mao * 114c579e15SShengtan Mao * This program is distributed in the hope that it will be useful, but WITHOUT 124c579e15SShengtan Mao * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 134c579e15SShengtan Mao * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 144c579e15SShengtan Mao * for more details. 154c579e15SShengtan Mao */ 164c579e15SShengtan Mao 174c579e15SShengtan Mao #include "qemu/osdep.h" 184c579e15SShengtan Mao #include "hw/sd/npcm7xx_sdhci.h" 194c579e15SShengtan Mao 20*907b5105SMarc-André Lureau #include "libqtest.h" 214c579e15SShengtan Mao #include "libqtest-single.h" 224c579e15SShengtan Mao #include "libqos/sdhci-cmd.h" 234c579e15SShengtan Mao 244c579e15SShengtan Mao #define NPCM7XX_REG_SIZE 0x100 254c579e15SShengtan Mao #define NPCM7XX_MMC_BA 0xF0842000 264c579e15SShengtan Mao #define NPCM7XX_BLK_SIZE 512 274c579e15SShengtan Mao #define NPCM7XX_TEST_IMAGE_SIZE (1 << 30) 284c579e15SShengtan Mao 294c579e15SShengtan Mao char *sd_path; 304c579e15SShengtan Mao 314c579e15SShengtan Mao static QTestState *setup_sd_card(void) 324c579e15SShengtan Mao { 334c579e15SShengtan Mao QTestState *qts = qtest_initf( 344c579e15SShengtan Mao "-machine kudo-bmc " 354c579e15SShengtan Mao "-device sd-card,drive=drive0 " 364c579e15SShengtan Mao "-drive id=drive0,if=none,file=%s,format=raw,auto-read-only=off", 374c579e15SShengtan Mao sd_path); 384c579e15SShengtan Mao 394c579e15SShengtan Mao qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_SWRST, SDHC_RESET_ALL); 404c579e15SShengtan Mao qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_CLKCON, 414c579e15SShengtan Mao SDHC_CLOCK_SDCLK_EN | SDHC_CLOCK_INT_STABLE | 424c579e15SShengtan Mao SDHC_CLOCK_INT_EN); 434c579e15SShengtan Mao sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_APP_CMD); 444c579e15SShengtan Mao sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x41200000, 0, (41 << 8)); 454c579e15SShengtan Mao sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_ALL_SEND_CID); 464c579e15SShengtan Mao sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR); 474c579e15SShengtan Mao sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x45670000, 0, 484c579e15SShengtan Mao SDHC_SELECT_DESELECT_CARD); 494c579e15SShengtan Mao 504c579e15SShengtan Mao return qts; 514c579e15SShengtan Mao } 524c579e15SShengtan Mao 534c579e15SShengtan Mao static void write_sdread(QTestState *qts, const char *msg) 544c579e15SShengtan Mao { 554c579e15SShengtan Mao int fd, ret; 564c579e15SShengtan Mao size_t len = strlen(msg); 574c579e15SShengtan Mao char *rmsg = g_malloc(len); 584c579e15SShengtan Mao 594c579e15SShengtan Mao /* write message to sd */ 604c579e15SShengtan Mao fd = open(sd_path, O_WRONLY); 614c579e15SShengtan Mao g_assert(fd >= 0); 624c579e15SShengtan Mao ret = write(fd, msg, len); 634c579e15SShengtan Mao close(fd); 644c579e15SShengtan Mao g_assert(ret == len); 654c579e15SShengtan Mao 664c579e15SShengtan Mao /* read message using sdhci */ 674c579e15SShengtan Mao ret = sdhci_read_cmd(qts, NPCM7XX_MMC_BA, rmsg, len); 684c579e15SShengtan Mao g_assert(ret == len); 694c579e15SShengtan Mao g_assert(!memcmp(rmsg, msg, len)); 704c579e15SShengtan Mao 714c579e15SShengtan Mao g_free(rmsg); 724c579e15SShengtan Mao } 734c579e15SShengtan Mao 744c579e15SShengtan Mao /* Check MMC can read values from sd */ 754c579e15SShengtan Mao static void test_read_sd(void) 764c579e15SShengtan Mao { 774c579e15SShengtan Mao QTestState *qts = setup_sd_card(); 784c579e15SShengtan Mao 794c579e15SShengtan Mao write_sdread(qts, "hello world"); 804c579e15SShengtan Mao write_sdread(qts, "goodbye"); 814c579e15SShengtan Mao 824c579e15SShengtan Mao qtest_quit(qts); 834c579e15SShengtan Mao } 844c579e15SShengtan Mao 854c579e15SShengtan Mao static void sdwrite_read(QTestState *qts, const char *msg) 864c579e15SShengtan Mao { 874c579e15SShengtan Mao int fd, ret; 884c579e15SShengtan Mao size_t len = strlen(msg); 894c579e15SShengtan Mao char *rmsg = g_malloc(len); 904c579e15SShengtan Mao 914c579e15SShengtan Mao /* write message using sdhci */ 924c579e15SShengtan Mao sdhci_write_cmd(qts, NPCM7XX_MMC_BA, msg, len, NPCM7XX_BLK_SIZE); 934c579e15SShengtan Mao 944c579e15SShengtan Mao /* read message from sd */ 954c579e15SShengtan Mao fd = open(sd_path, O_RDONLY); 964c579e15SShengtan Mao g_assert(fd >= 0); 974c579e15SShengtan Mao ret = read(fd, rmsg, len); 984c579e15SShengtan Mao close(fd); 994c579e15SShengtan Mao g_assert(ret == len); 1004c579e15SShengtan Mao 1014c579e15SShengtan Mao g_assert(!memcmp(rmsg, msg, len)); 1024c579e15SShengtan Mao 1034c579e15SShengtan Mao g_free(rmsg); 1044c579e15SShengtan Mao } 1054c579e15SShengtan Mao 1064c579e15SShengtan Mao /* Check MMC can write values to sd */ 1074c579e15SShengtan Mao static void test_write_sd(void) 1084c579e15SShengtan Mao { 1094c579e15SShengtan Mao QTestState *qts = setup_sd_card(); 1104c579e15SShengtan Mao 1114c579e15SShengtan Mao sdwrite_read(qts, "hello world"); 1124c579e15SShengtan Mao sdwrite_read(qts, "goodbye"); 1134c579e15SShengtan Mao 1144c579e15SShengtan Mao qtest_quit(qts); 1154c579e15SShengtan Mao } 1164c579e15SShengtan Mao 1174c579e15SShengtan Mao /* Check SDHCI has correct default values. */ 1184c579e15SShengtan Mao static void test_reset(void) 1194c579e15SShengtan Mao { 1204c579e15SShengtan Mao QTestState *qts = qtest_init("-machine kudo-bmc"); 1214c579e15SShengtan Mao uint64_t addr = NPCM7XX_MMC_BA; 1224c579e15SShengtan Mao uint64_t end_addr = addr + NPCM7XX_REG_SIZE; 1234c579e15SShengtan Mao uint16_t prstvals_resets[] = {NPCM7XX_PRSTVALS_0_RESET, 1244c579e15SShengtan Mao NPCM7XX_PRSTVALS_1_RESET, 1254c579e15SShengtan Mao 0, 1264c579e15SShengtan Mao NPCM7XX_PRSTVALS_3_RESET, 1274c579e15SShengtan Mao 0, 1284c579e15SShengtan Mao 0}; 1294c579e15SShengtan Mao int i; 1304c579e15SShengtan Mao uint32_t mask; 1314c579e15SShengtan Mao 1324c579e15SShengtan Mao while (addr < end_addr) { 1334c579e15SShengtan Mao switch (addr - NPCM7XX_MMC_BA) { 1344c579e15SShengtan Mao case SDHC_PRNSTS: 1354c579e15SShengtan Mao /* 1364c579e15SShengtan Mao * ignores bits 20 to 24: they are changed when reading registers 1374c579e15SShengtan Mao */ 1384c579e15SShengtan Mao mask = 0x1f00000; 1394c579e15SShengtan Mao g_assert_cmphex(qtest_readl(qts, addr) | mask, ==, 1404c579e15SShengtan Mao NPCM7XX_PRSNTS_RESET | mask); 1414c579e15SShengtan Mao addr += 4; 1424c579e15SShengtan Mao break; 1434c579e15SShengtan Mao case SDHC_BLKGAP: 1444c579e15SShengtan Mao g_assert_cmphex(qtest_readb(qts, addr), ==, NPCM7XX_BLKGAP_RESET); 1454c579e15SShengtan Mao addr += 1; 1464c579e15SShengtan Mao break; 1474c579e15SShengtan Mao case SDHC_CAPAB: 1484c579e15SShengtan Mao g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_CAPAB_RESET); 1494c579e15SShengtan Mao addr += 8; 1504c579e15SShengtan Mao break; 1514c579e15SShengtan Mao case SDHC_MAXCURR: 1524c579e15SShengtan Mao g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_MAXCURR_RESET); 1534c579e15SShengtan Mao addr += 8; 1544c579e15SShengtan Mao break; 1554c579e15SShengtan Mao case SDHC_HCVER: 1564c579e15SShengtan Mao g_assert_cmphex(qtest_readw(qts, addr), ==, NPCM7XX_HCVER_RESET); 1574c579e15SShengtan Mao addr += 2; 1584c579e15SShengtan Mao break; 1594c579e15SShengtan Mao case NPCM7XX_PRSTVALS: 1604c579e15SShengtan Mao for (i = 0; i < NPCM7XX_PRSTVALS_SIZE; ++i) { 1614c579e15SShengtan Mao g_assert_cmphex(qtest_readw(qts, addr + 2 * i), ==, 1624c579e15SShengtan Mao prstvals_resets[i]); 1634c579e15SShengtan Mao } 1644c579e15SShengtan Mao addr += NPCM7XX_PRSTVALS_SIZE * 2; 1654c579e15SShengtan Mao break; 1664c579e15SShengtan Mao default: 1674c579e15SShengtan Mao g_assert_cmphex(qtest_readb(qts, addr), ==, 0); 1684c579e15SShengtan Mao addr += 1; 1694c579e15SShengtan Mao } 1704c579e15SShengtan Mao } 1714c579e15SShengtan Mao 1724c579e15SShengtan Mao qtest_quit(qts); 1734c579e15SShengtan Mao } 1744c579e15SShengtan Mao 1754c579e15SShengtan Mao static void drive_destroy(void) 1764c579e15SShengtan Mao { 1774c579e15SShengtan Mao unlink(sd_path); 1784c579e15SShengtan Mao g_free(sd_path); 1794c579e15SShengtan Mao } 1804c579e15SShengtan Mao 1814c579e15SShengtan Mao static void drive_create(void) 1824c579e15SShengtan Mao { 1834c579e15SShengtan Mao int fd, ret; 1844c579e15SShengtan Mao GError *error = NULL; 1854c579e15SShengtan Mao 1864c579e15SShengtan Mao /* Create a temporary raw image */ 1874c579e15SShengtan Mao fd = g_file_open_tmp("sdhci_XXXXXX", &sd_path, &error); 1884c579e15SShengtan Mao if (fd == -1) { 1894c579e15SShengtan Mao fprintf(stderr, "unable to create sdhci file: %s\n", error->message); 1904c579e15SShengtan Mao g_error_free(error); 1914c579e15SShengtan Mao } 1924c579e15SShengtan Mao g_assert(sd_path != NULL); 1934c579e15SShengtan Mao 1944c579e15SShengtan Mao ret = ftruncate(fd, NPCM7XX_TEST_IMAGE_SIZE); 1954c579e15SShengtan Mao g_assert_cmpint(ret, ==, 0); 1964c579e15SShengtan Mao g_message("%s", sd_path); 1974c579e15SShengtan Mao close(fd); 1984c579e15SShengtan Mao } 1994c579e15SShengtan Mao 2004c579e15SShengtan Mao int main(int argc, char **argv) 2014c579e15SShengtan Mao { 2024c579e15SShengtan Mao int ret; 2034c579e15SShengtan Mao 2044c579e15SShengtan Mao drive_create(); 2054c579e15SShengtan Mao 2064c579e15SShengtan Mao g_test_init(&argc, &argv, NULL); 2074c579e15SShengtan Mao 2084c579e15SShengtan Mao qtest_add_func("npcm7xx_sdhci/reset", test_reset); 2094c579e15SShengtan Mao qtest_add_func("npcm7xx_sdhci/write_sd", test_write_sd); 2104c579e15SShengtan Mao qtest_add_func("npcm7xx_sdhci/read_sd", test_read_sd); 2114c579e15SShengtan Mao 2124c579e15SShengtan Mao ret = g_test_run(); 2134c579e15SShengtan Mao drive_destroy(); 2144c579e15SShengtan Mao return ret; 2154c579e15SShengtan Mao } 216