1 /* 2 * QTests for NPCM7xx SD-3.0 / MMC-4.51 Host Controller 3 * 4 * Copyright (c) 2022 Google LLC 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * for more details. 15 */ 16 17 #include "qemu/osdep.h" 18 #include "hw/sd/npcm7xx_sdhci.h" 19 20 #include "libqos/libqtest.h" 21 #include "libqtest-single.h" 22 #include "libqos/sdhci-cmd.h" 23 24 #define NPCM7XX_REG_SIZE 0x100 25 #define NPCM7XX_MMC_BA 0xF0842000 26 #define NPCM7XX_BLK_SIZE 512 27 #define NPCM7XX_TEST_IMAGE_SIZE (1 << 30) 28 29 char *sd_path; 30 31 static QTestState *setup_sd_card(void) 32 { 33 QTestState *qts = qtest_initf( 34 "-machine kudo-bmc " 35 "-device sd-card,drive=drive0 " 36 "-drive id=drive0,if=none,file=%s,format=raw,auto-read-only=off", 37 sd_path); 38 39 qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_SWRST, SDHC_RESET_ALL); 40 qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_CLKCON, 41 SDHC_CLOCK_SDCLK_EN | SDHC_CLOCK_INT_STABLE | 42 SDHC_CLOCK_INT_EN); 43 sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_APP_CMD); 44 sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x41200000, 0, (41 << 8)); 45 sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_ALL_SEND_CID); 46 sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR); 47 sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x45670000, 0, 48 SDHC_SELECT_DESELECT_CARD); 49 50 return qts; 51 } 52 53 static void write_sdread(QTestState *qts, const char *msg) 54 { 55 int fd, ret; 56 size_t len = strlen(msg); 57 char *rmsg = g_malloc(len); 58 59 /* write message to sd */ 60 fd = open(sd_path, O_WRONLY); 61 g_assert(fd >= 0); 62 ret = write(fd, msg, len); 63 close(fd); 64 g_assert(ret == len); 65 66 /* read message using sdhci */ 67 ret = sdhci_read_cmd(qts, NPCM7XX_MMC_BA, rmsg, len); 68 g_assert(ret == len); 69 g_assert(!memcmp(rmsg, msg, len)); 70 71 g_free(rmsg); 72 } 73 74 /* Check MMC can read values from sd */ 75 static void test_read_sd(void) 76 { 77 QTestState *qts = setup_sd_card(); 78 79 write_sdread(qts, "hello world"); 80 write_sdread(qts, "goodbye"); 81 82 qtest_quit(qts); 83 } 84 85 static void sdwrite_read(QTestState *qts, const char *msg) 86 { 87 int fd, ret; 88 size_t len = strlen(msg); 89 char *rmsg = g_malloc(len); 90 91 /* write message using sdhci */ 92 sdhci_write_cmd(qts, NPCM7XX_MMC_BA, msg, len, NPCM7XX_BLK_SIZE); 93 94 /* read message from sd */ 95 fd = open(sd_path, O_RDONLY); 96 g_assert(fd >= 0); 97 ret = read(fd, rmsg, len); 98 close(fd); 99 g_assert(ret == len); 100 101 g_assert(!memcmp(rmsg, msg, len)); 102 103 g_free(rmsg); 104 } 105 106 /* Check MMC can write values to sd */ 107 static void test_write_sd(void) 108 { 109 QTestState *qts = setup_sd_card(); 110 111 sdwrite_read(qts, "hello world"); 112 sdwrite_read(qts, "goodbye"); 113 114 qtest_quit(qts); 115 } 116 117 /* Check SDHCI has correct default values. */ 118 static void test_reset(void) 119 { 120 QTestState *qts = qtest_init("-machine kudo-bmc"); 121 uint64_t addr = NPCM7XX_MMC_BA; 122 uint64_t end_addr = addr + NPCM7XX_REG_SIZE; 123 uint16_t prstvals_resets[] = {NPCM7XX_PRSTVALS_0_RESET, 124 NPCM7XX_PRSTVALS_1_RESET, 125 0, 126 NPCM7XX_PRSTVALS_3_RESET, 127 0, 128 0}; 129 int i; 130 uint32_t mask; 131 132 while (addr < end_addr) { 133 switch (addr - NPCM7XX_MMC_BA) { 134 case SDHC_PRNSTS: 135 /* 136 * ignores bits 20 to 24: they are changed when reading registers 137 */ 138 mask = 0x1f00000; 139 g_assert_cmphex(qtest_readl(qts, addr) | mask, ==, 140 NPCM7XX_PRSNTS_RESET | mask); 141 addr += 4; 142 break; 143 case SDHC_BLKGAP: 144 g_assert_cmphex(qtest_readb(qts, addr), ==, NPCM7XX_BLKGAP_RESET); 145 addr += 1; 146 break; 147 case SDHC_CAPAB: 148 g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_CAPAB_RESET); 149 addr += 8; 150 break; 151 case SDHC_MAXCURR: 152 g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_MAXCURR_RESET); 153 addr += 8; 154 break; 155 case SDHC_HCVER: 156 g_assert_cmphex(qtest_readw(qts, addr), ==, NPCM7XX_HCVER_RESET); 157 addr += 2; 158 break; 159 case NPCM7XX_PRSTVALS: 160 for (i = 0; i < NPCM7XX_PRSTVALS_SIZE; ++i) { 161 g_assert_cmphex(qtest_readw(qts, addr + 2 * i), ==, 162 prstvals_resets[i]); 163 } 164 addr += NPCM7XX_PRSTVALS_SIZE * 2; 165 break; 166 default: 167 g_assert_cmphex(qtest_readb(qts, addr), ==, 0); 168 addr += 1; 169 } 170 } 171 172 qtest_quit(qts); 173 } 174 175 static void drive_destroy(void) 176 { 177 unlink(sd_path); 178 g_free(sd_path); 179 } 180 181 static void drive_create(void) 182 { 183 int fd, ret; 184 GError *error = NULL; 185 186 /* Create a temporary raw image */ 187 fd = g_file_open_tmp("sdhci_XXXXXX", &sd_path, &error); 188 if (fd == -1) { 189 fprintf(stderr, "unable to create sdhci file: %s\n", error->message); 190 g_error_free(error); 191 } 192 g_assert(sd_path != NULL); 193 194 ret = ftruncate(fd, NPCM7XX_TEST_IMAGE_SIZE); 195 g_assert_cmpint(ret, ==, 0); 196 g_message("%s", sd_path); 197 close(fd); 198 } 199 200 int main(int argc, char **argv) 201 { 202 int ret; 203 204 drive_create(); 205 206 g_test_init(&argc, &argv, NULL); 207 208 qtest_add_func("npcm7xx_sdhci/reset", test_reset); 209 qtest_add_func("npcm7xx_sdhci/write_sd", test_write_sd); 210 qtest_add_func("npcm7xx_sdhci/read_sd", test_read_sd); 211 212 ret = g_test_run(); 213 drive_destroy(); 214 return ret; 215 } 216