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
setup_sd_card(void)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
write_sdread(QTestState * qts,const char * msg)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 */
test_read_sd(void)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
sdwrite_read(QTestState * qts,const char * msg)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 */
test_write_sd(void)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. */
test_reset(void)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
drive_destroy(void)179 static void drive_destroy(void)
180 {
181 unlink(sd_path);
182 g_free(sd_path);
183 }
184
drive_create(void)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
main(int argc,char ** argv)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