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     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