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 20907b5105SMarc-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 27284ad5e7SHao Wu #define NPCM7XX_TEST_IMAGE_SIZE (1 << 20) 284c579e15SShengtan Mao 294c579e15SShengtan Mao char *sd_path; 304c579e15SShengtan Mao 314c579e15SShengtan Mao static QTestState *setup_sd_card(void) 324c579e15SShengtan Mao { 33*3baae281SPhilippe Mathieu-Daudé uint16_t rca; 34*3baae281SPhilippe Mathieu-Daudé 354c579e15SShengtan Mao QTestState *qts = qtest_initf( 364c579e15SShengtan Mao "-machine kudo-bmc " 374c579e15SShengtan Mao "-device sd-card,drive=drive0 " 384c579e15SShengtan Mao "-drive id=drive0,if=none,file=%s,format=raw,auto-read-only=off", 394c579e15SShengtan Mao sd_path); 404c579e15SShengtan Mao 414c579e15SShengtan Mao qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_SWRST, SDHC_RESET_ALL); 424c579e15SShengtan Mao qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_CLKCON, 434c579e15SShengtan Mao SDHC_CLOCK_SDCLK_EN | SDHC_CLOCK_INT_STABLE | 444c579e15SShengtan Mao SDHC_CLOCK_INT_EN); 454c579e15SShengtan Mao sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_APP_CMD); 464c579e15SShengtan Mao sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x41200000, 0, (41 << 8)); 474c579e15SShengtan Mao sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_ALL_SEND_CID); 48*3baae281SPhilippe Mathieu-Daudé sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR 49*3baae281SPhilippe Mathieu-Daudé | SDHC_CMD_RESPONSE); 50*3baae281SPhilippe Mathieu-Daudé rca = qtest_readl(qts, NPCM7XX_MMC_BA + SDHC_RSPREG0) >> 16; 51*3baae281SPhilippe Mathieu-Daudé sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, rca << 16, 0, 524c579e15SShengtan Mao SDHC_SELECT_DESELECT_CARD); 534c579e15SShengtan Mao 544c579e15SShengtan Mao return qts; 554c579e15SShengtan Mao } 564c579e15SShengtan Mao 574c579e15SShengtan Mao static void write_sdread(QTestState *qts, const char *msg) 584c579e15SShengtan Mao { 594c579e15SShengtan Mao int fd, ret; 604c579e15SShengtan Mao size_t len = strlen(msg); 614c579e15SShengtan Mao char *rmsg = g_malloc(len); 624c579e15SShengtan Mao 634c579e15SShengtan Mao /* write message to sd */ 644c579e15SShengtan Mao fd = open(sd_path, O_WRONLY); 654c579e15SShengtan Mao g_assert(fd >= 0); 664c579e15SShengtan Mao ret = write(fd, msg, len); 674c579e15SShengtan Mao close(fd); 684c579e15SShengtan Mao g_assert(ret == len); 694c579e15SShengtan Mao 704c579e15SShengtan Mao /* read message using sdhci */ 714c579e15SShengtan Mao ret = sdhci_read_cmd(qts, NPCM7XX_MMC_BA, rmsg, len); 724c579e15SShengtan Mao g_assert(ret == len); 734c579e15SShengtan Mao g_assert(!memcmp(rmsg, msg, len)); 744c579e15SShengtan Mao 754c579e15SShengtan Mao g_free(rmsg); 764c579e15SShengtan Mao } 774c579e15SShengtan Mao 784c579e15SShengtan Mao /* Check MMC can read values from sd */ 794c579e15SShengtan Mao static void test_read_sd(void) 804c579e15SShengtan Mao { 814c579e15SShengtan Mao QTestState *qts = setup_sd_card(); 824c579e15SShengtan Mao 834c579e15SShengtan Mao write_sdread(qts, "hello world"); 844c579e15SShengtan Mao write_sdread(qts, "goodbye"); 854c579e15SShengtan Mao 864c579e15SShengtan Mao qtest_quit(qts); 874c579e15SShengtan Mao } 884c579e15SShengtan Mao 894c579e15SShengtan Mao static void sdwrite_read(QTestState *qts, const char *msg) 904c579e15SShengtan Mao { 914c579e15SShengtan Mao int fd, ret; 924c579e15SShengtan Mao size_t len = strlen(msg); 934c579e15SShengtan Mao char *rmsg = g_malloc(len); 944c579e15SShengtan Mao 954c579e15SShengtan Mao /* write message using sdhci */ 964c579e15SShengtan Mao sdhci_write_cmd(qts, NPCM7XX_MMC_BA, msg, len, NPCM7XX_BLK_SIZE); 974c579e15SShengtan Mao 984c579e15SShengtan Mao /* read message from sd */ 994c579e15SShengtan Mao fd = open(sd_path, O_RDONLY); 1004c579e15SShengtan Mao g_assert(fd >= 0); 1014c579e15SShengtan Mao ret = read(fd, rmsg, len); 1024c579e15SShengtan Mao close(fd); 1034c579e15SShengtan Mao g_assert(ret == len); 1044c579e15SShengtan Mao 1054c579e15SShengtan Mao g_assert(!memcmp(rmsg, msg, len)); 1064c579e15SShengtan Mao 1074c579e15SShengtan Mao g_free(rmsg); 1084c579e15SShengtan Mao } 1094c579e15SShengtan Mao 1104c579e15SShengtan Mao /* Check MMC can write values to sd */ 1114c579e15SShengtan Mao static void test_write_sd(void) 1124c579e15SShengtan Mao { 1134c579e15SShengtan Mao QTestState *qts = setup_sd_card(); 1144c579e15SShengtan Mao 1154c579e15SShengtan Mao sdwrite_read(qts, "hello world"); 1164c579e15SShengtan Mao sdwrite_read(qts, "goodbye"); 1174c579e15SShengtan Mao 1184c579e15SShengtan Mao qtest_quit(qts); 1194c579e15SShengtan Mao } 1204c579e15SShengtan Mao 1214c579e15SShengtan Mao /* Check SDHCI has correct default values. */ 1224c579e15SShengtan Mao static void test_reset(void) 1234c579e15SShengtan Mao { 1244c579e15SShengtan Mao QTestState *qts = qtest_init("-machine kudo-bmc"); 1254c579e15SShengtan Mao uint64_t addr = NPCM7XX_MMC_BA; 1264c579e15SShengtan Mao uint64_t end_addr = addr + NPCM7XX_REG_SIZE; 1274c579e15SShengtan Mao uint16_t prstvals_resets[] = {NPCM7XX_PRSTVALS_0_RESET, 1284c579e15SShengtan Mao NPCM7XX_PRSTVALS_1_RESET, 1294c579e15SShengtan Mao 0, 1304c579e15SShengtan Mao NPCM7XX_PRSTVALS_3_RESET, 1314c579e15SShengtan Mao 0, 1324c579e15SShengtan Mao 0}; 1334c579e15SShengtan Mao int i; 1344c579e15SShengtan Mao uint32_t mask; 1354c579e15SShengtan Mao 1364c579e15SShengtan Mao while (addr < end_addr) { 1374c579e15SShengtan Mao switch (addr - NPCM7XX_MMC_BA) { 1384c579e15SShengtan Mao case SDHC_PRNSTS: 1394c579e15SShengtan Mao /* 1404c579e15SShengtan Mao * ignores bits 20 to 24: they are changed when reading registers 1414c579e15SShengtan Mao */ 1424c579e15SShengtan Mao mask = 0x1f00000; 1434c579e15SShengtan Mao g_assert_cmphex(qtest_readl(qts, addr) | mask, ==, 1444c579e15SShengtan Mao NPCM7XX_PRSNTS_RESET | mask); 1454c579e15SShengtan Mao addr += 4; 1464c579e15SShengtan Mao break; 1474c579e15SShengtan Mao case SDHC_BLKGAP: 1484c579e15SShengtan Mao g_assert_cmphex(qtest_readb(qts, addr), ==, NPCM7XX_BLKGAP_RESET); 1494c579e15SShengtan Mao addr += 1; 1504c579e15SShengtan Mao break; 1514c579e15SShengtan Mao case SDHC_CAPAB: 1524c579e15SShengtan Mao g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_CAPAB_RESET); 1534c579e15SShengtan Mao addr += 8; 1544c579e15SShengtan Mao break; 1554c579e15SShengtan Mao case SDHC_MAXCURR: 1564c579e15SShengtan Mao g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_MAXCURR_RESET); 1574c579e15SShengtan Mao addr += 8; 1584c579e15SShengtan Mao break; 1594c579e15SShengtan Mao case SDHC_HCVER: 1604c579e15SShengtan Mao g_assert_cmphex(qtest_readw(qts, addr), ==, NPCM7XX_HCVER_RESET); 1614c579e15SShengtan Mao addr += 2; 1624c579e15SShengtan Mao break; 1634c579e15SShengtan Mao case NPCM7XX_PRSTVALS: 1644c579e15SShengtan Mao for (i = 0; i < NPCM7XX_PRSTVALS_SIZE; ++i) { 1654c579e15SShengtan Mao g_assert_cmphex(qtest_readw(qts, addr + 2 * i), ==, 1664c579e15SShengtan Mao prstvals_resets[i]); 1674c579e15SShengtan Mao } 1684c579e15SShengtan Mao addr += NPCM7XX_PRSTVALS_SIZE * 2; 1694c579e15SShengtan Mao break; 1704c579e15SShengtan Mao default: 1714c579e15SShengtan Mao g_assert_cmphex(qtest_readb(qts, addr), ==, 0); 1724c579e15SShengtan Mao addr += 1; 1734c579e15SShengtan Mao } 1744c579e15SShengtan Mao } 1754c579e15SShengtan Mao 1764c579e15SShengtan Mao qtest_quit(qts); 1774c579e15SShengtan Mao } 1784c579e15SShengtan Mao 1794c579e15SShengtan Mao static void drive_destroy(void) 1804c579e15SShengtan Mao { 1814c579e15SShengtan Mao unlink(sd_path); 1824c579e15SShengtan Mao g_free(sd_path); 1834c579e15SShengtan Mao } 1844c579e15SShengtan Mao 1854c579e15SShengtan Mao static void drive_create(void) 1864c579e15SShengtan Mao { 1874c579e15SShengtan Mao int fd, ret; 1884c579e15SShengtan Mao GError *error = NULL; 1894c579e15SShengtan Mao 1904c579e15SShengtan Mao /* Create a temporary raw image */ 1914c579e15SShengtan Mao fd = g_file_open_tmp("sdhci_XXXXXX", &sd_path, &error); 1924c579e15SShengtan Mao if (fd == -1) { 1934c579e15SShengtan Mao fprintf(stderr, "unable to create sdhci file: %s\n", error->message); 1944c579e15SShengtan Mao g_error_free(error); 1954c579e15SShengtan Mao } 1964c579e15SShengtan Mao g_assert(sd_path != NULL); 1974c579e15SShengtan Mao 1984c579e15SShengtan Mao ret = ftruncate(fd, NPCM7XX_TEST_IMAGE_SIZE); 1994c579e15SShengtan Mao g_assert_cmpint(ret, ==, 0); 2004c579e15SShengtan Mao g_message("%s", sd_path); 2014c579e15SShengtan Mao close(fd); 2024c579e15SShengtan Mao } 2034c579e15SShengtan Mao 2044c579e15SShengtan Mao int main(int argc, char **argv) 2054c579e15SShengtan Mao { 2064c579e15SShengtan Mao int ret; 2074c579e15SShengtan Mao 2084c579e15SShengtan Mao drive_create(); 2094c579e15SShengtan Mao 2104c579e15SShengtan Mao g_test_init(&argc, &argv, NULL); 2114c579e15SShengtan Mao 2124c579e15SShengtan Mao qtest_add_func("npcm7xx_sdhci/reset", test_reset); 2134c579e15SShengtan Mao qtest_add_func("npcm7xx_sdhci/write_sd", test_write_sd); 2144c579e15SShengtan Mao qtest_add_func("npcm7xx_sdhci/read_sd", test_read_sd); 2154c579e15SShengtan Mao 2164c579e15SShengtan Mao ret = g_test_run(); 2174c579e15SShengtan Mao drive_destroy(); 2184c579e15SShengtan Mao return ret; 2194c579e15SShengtan Mao } 220