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 "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 << 20) 28 29 char *sd_path; 30 31 static QTestState *setup_sd_card(void) 32 { 33 uint16_t rca; 34 35 QTestState *qts = qtest_initf( 36 "-machine kudo-bmc " 37 "-device sd-card,drive=drive0 " 38 "-drive id=drive0,if=none,file=%s,format=raw,auto-read-only=off", 39 sd_path); 40 41 qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_SWRST, SDHC_RESET_ALL); 42 qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_CLKCON, 43 SDHC_CLOCK_SDCLK_EN | SDHC_CLOCK_INT_STABLE | 44 SDHC_CLOCK_INT_EN); 45 sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_APP_CMD); 46 sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x41200000, 0, (41 << 8)); 47 sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_ALL_SEND_CID); 48 sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR 49 | SDHC_CMD_RESPONSE); 50 rca = qtest_readl(qts, NPCM7XX_MMC_BA + SDHC_RSPREG0) >> 16; 51 sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, rca << 16, 0, 52 SDHC_SELECT_DESELECT_CARD); 53 54 return qts; 55 } 56 57 static void write_sdread(QTestState *qts, const char *msg) 58 { 59 int fd, ret; 60 size_t len = strlen(msg); 61 char *rmsg = g_malloc(len); 62 63 /* write message to sd */ 64 fd = open(sd_path, O_WRONLY); 65 g_assert(fd >= 0); 66 ret = write(fd, msg, len); 67 close(fd); 68 g_assert(ret == len); 69 70 /* read message using sdhci */ 71 ret = sdhci_read_cmd(qts, NPCM7XX_MMC_BA, rmsg, len); 72 g_assert(ret == len); 73 g_assert(!memcmp(rmsg, msg, len)); 74 75 g_free(rmsg); 76 } 77 78 /* Check MMC can read values from sd */ 79 static void test_read_sd(void) 80 { 81 QTestState *qts = setup_sd_card(); 82 83 write_sdread(qts, "hello world"); 84 write_sdread(qts, "goodbye"); 85 86 qtest_quit(qts); 87 } 88 89 static void sdwrite_read(QTestState *qts, const char *msg) 90 { 91 int fd, ret; 92 size_t len = strlen(msg); 93 char *rmsg = g_malloc(len); 94 95 /* write message using sdhci */ 96 sdhci_write_cmd(qts, NPCM7XX_MMC_BA, msg, len, NPCM7XX_BLK_SIZE); 97 98 /* read message from sd */ 99 fd = open(sd_path, O_RDONLY); 100 g_assert(fd >= 0); 101 ret = read(fd, rmsg, len); 102 close(fd); 103 g_assert(ret == len); 104 105 g_assert(!memcmp(rmsg, msg, len)); 106 107 g_free(rmsg); 108 } 109 110 /* Check MMC can write values to sd */ 111 static void test_write_sd(void) 112 { 113 QTestState *qts = setup_sd_card(); 114 115 sdwrite_read(qts, "hello world"); 116 sdwrite_read(qts, "goodbye"); 117 118 qtest_quit(qts); 119 } 120 121 /* Check SDHCI has correct default values. */ 122 static void test_reset(void) 123 { 124 QTestState *qts = qtest_init("-machine kudo-bmc"); 125 uint64_t addr = NPCM7XX_MMC_BA; 126 uint64_t end_addr = addr + NPCM7XX_REG_SIZE; 127 uint16_t prstvals_resets[] = {NPCM7XX_PRSTVALS_0_RESET, 128 NPCM7XX_PRSTVALS_1_RESET, 129 0, 130 NPCM7XX_PRSTVALS_3_RESET, 131 0, 132 0}; 133 int i; 134 uint32_t mask; 135 136 while (addr < end_addr) { 137 switch (addr - NPCM7XX_MMC_BA) { 138 case SDHC_PRNSTS: 139 /* 140 * ignores bits 20 to 24: they are changed when reading registers 141 */ 142 mask = 0x1f00000; 143 g_assert_cmphex(qtest_readl(qts, addr) | mask, ==, 144 NPCM7XX_PRSNTS_RESET | mask); 145 addr += 4; 146 break; 147 case SDHC_BLKGAP: 148 g_assert_cmphex(qtest_readb(qts, addr), ==, NPCM7XX_BLKGAP_RESET); 149 addr += 1; 150 break; 151 case SDHC_CAPAB: 152 g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_CAPAB_RESET); 153 addr += 8; 154 break; 155 case SDHC_MAXCURR: 156 g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_MAXCURR_RESET); 157 addr += 8; 158 break; 159 case SDHC_HCVER: 160 g_assert_cmphex(qtest_readw(qts, addr), ==, NPCM7XX_HCVER_RESET); 161 addr += 2; 162 break; 163 case NPCM7XX_PRSTVALS: 164 for (i = 0; i < NPCM7XX_PRSTVALS_SIZE; ++i) { 165 g_assert_cmphex(qtest_readw(qts, addr + 2 * i), ==, 166 prstvals_resets[i]); 167 } 168 addr += NPCM7XX_PRSTVALS_SIZE * 2; 169 break; 170 default: 171 g_assert_cmphex(qtest_readb(qts, addr), ==, 0); 172 addr += 1; 173 } 174 } 175 176 qtest_quit(qts); 177 } 178 179 static void drive_destroy(void) 180 { 181 unlink(sd_path); 182 g_free(sd_path); 183 } 184 185 static void drive_create(void) 186 { 187 int fd, ret; 188 GError *error = NULL; 189 190 /* Create a temporary raw image */ 191 fd = g_file_open_tmp("sdhci_XXXXXX", &sd_path, &error); 192 if (fd == -1) { 193 fprintf(stderr, "unable to create sdhci file: %s\n", error->message); 194 g_error_free(error); 195 } 196 g_assert(sd_path != NULL); 197 198 ret = ftruncate(fd, NPCM7XX_TEST_IMAGE_SIZE); 199 g_assert_cmpint(ret, ==, 0); 200 g_message("%s", sd_path); 201 close(fd); 202 } 203 204 int main(int argc, char **argv) 205 { 206 int ret; 207 208 drive_create(); 209 210 g_test_init(&argc, &argv, NULL); 211 212 qtest_add_func("npcm7xx_sdhci/reset", test_reset); 213 qtest_add_func("npcm7xx_sdhci/write_sd", test_write_sd); 214 qtest_add_func("npcm7xx_sdhci/read_sd", test_read_sd); 215 216 ret = g_test_run(); 217 drive_destroy(); 218 return ret; 219 } 220