2 * QTest testcase for SDHCI controllers
4 * Written by Philippe Mathieu-Daudé <f4bug@amsat.org>
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
8 * SPDX-License-Identifier: GPL-2.0-or-later
10 #include "qemu/osdep.h"
11 #include "hw/registerfields.h"
13 #include "libqos/pci-pc.h"
14 #include "hw/pci/pci.h"
16 #define SDHC_CAPAB 0x40
17 FIELD(SDHC_CAPAB
, BASECLKFREQ
, 8, 8); /* since v2 */
18 FIELD(SDHC_CAPAB
, SDMA
, 22, 1);
19 FIELD(SDHC_CAPAB
, SDR
, 32, 3); /* since v3 */
20 FIELD(SDHC_CAPAB
, DRIVER
, 36, 3); /* since v3 */
21 #define SDHC_HCVER 0xFE
23 static const struct sdhci_t
{
24 const char *arch
, *machine
;
35 uint16_t vendor_id
, device_id
;
40 {-1, 2, 0, {1, 0x057834b4} },
41 .pci
= { PCI_VENDOR_ID_REDHAT
, PCI_DEVICE_ID_REDHAT_SDHCI
} },
45 {0x12510000, 2, 0, {1, 0x5e80080} } },
49 {0x02190000, 3, 0, {1, 0x057834b4} } },
53 {0x3f300000, 3, 52, {0, 0x052134b4} } },
56 { "arm", "xilinx-zynq-a9", /* Datasheet: UG585 (v1.12.1) */
57 {0xe0100000, 2, 0, {1, 0x69ec0080} } },
60 { "aarch64", "xlnx-zcu102", /* Datasheet: UG1085 (v1.7) */
61 {0xff160000, 3, 0, {1, 0x280737ec6481} } },
65 typedef struct QSDHCI
{
76 static uint16_t sdhci_readw(QSDHCI
*s
, uint32_t reg
)
81 val
= qpci_io_readw(s
->pci
.dev
, s
->mem_bar
, reg
);
83 val
= qtest_readw(global_qtest
, s
->addr
+ reg
);
89 static uint64_t sdhci_readq(QSDHCI
*s
, uint32_t reg
)
94 val
= qpci_io_readq(s
->pci
.dev
, s
->mem_bar
, reg
);
96 val
= qtest_readq(global_qtest
, s
->addr
+ reg
);
102 static void sdhci_writeq(QSDHCI
*s
, uint32_t reg
, uint64_t val
)
105 qpci_io_writeq(s
->pci
.dev
, s
->mem_bar
, reg
, val
);
107 qtest_writeq(global_qtest
, s
->addr
+ reg
, val
);
111 static void check_specs_version(QSDHCI
*s
, uint8_t version
)
115 v
= sdhci_readw(s
, SDHC_HCVER
);
118 g_assert_cmpuint(v
, ==, version
);
121 static void check_capab_capareg(QSDHCI
*s
, uint64_t expec_capab
)
125 capab
= sdhci_readq(s
, SDHC_CAPAB
);
126 g_assert_cmphex(capab
, ==, expec_capab
);
129 static void check_capab_readonly(QSDHCI
*s
)
131 const uint64_t vrand
= 0x123456789abcdef;
132 uint64_t capab0
, capab1
;
134 capab0
= sdhci_readq(s
, SDHC_CAPAB
);
135 g_assert_cmpuint(capab0
, !=, vrand
);
137 sdhci_writeq(s
, SDHC_CAPAB
, vrand
);
138 capab1
= sdhci_readq(s
, SDHC_CAPAB
);
139 g_assert_cmpuint(capab1
, !=, vrand
);
140 g_assert_cmpuint(capab1
, ==, capab0
);
143 static void check_capab_baseclock(QSDHCI
*s
, uint8_t expec_freq
)
145 uint64_t capab
, capab_freq
;
150 capab
= sdhci_readq(s
, SDHC_CAPAB
);
151 capab_freq
= FIELD_EX64(capab
, SDHC_CAPAB
, BASECLKFREQ
);
152 g_assert_cmpuint(capab_freq
, ==, expec_freq
);
155 static void check_capab_sdma(QSDHCI
*s
, bool supported
)
157 uint64_t capab
, capab_sdma
;
159 capab
= sdhci_readq(s
, SDHC_CAPAB
);
160 capab_sdma
= FIELD_EX64(capab
, SDHC_CAPAB
, SDMA
);
161 g_assert_cmpuint(capab_sdma
, ==, supported
);
164 static void check_capab_v3(QSDHCI
*s
, uint8_t version
)
166 uint64_t capab
, capab_v3
;
169 /* before v3 those fields are RESERVED */
170 capab
= sdhci_readq(s
, SDHC_CAPAB
);
171 capab_v3
= FIELD_EX64(capab
, SDHC_CAPAB
, SDR
);
172 g_assert_cmpuint(capab_v3
, ==, 0);
173 capab_v3
= FIELD_EX64(capab
, SDHC_CAPAB
, DRIVER
);
174 g_assert_cmpuint(capab_v3
, ==, 0);
178 static QSDHCI
*machine_start(const struct sdhci_t
*test
)
180 QSDHCI
*s
= g_new0(QSDHCI
, 1);
182 if (test
->pci
.vendor_id
) {
184 uint16_t vendor_id
, device_id
;
187 global_qtest
= qtest_startf("-machine %s -device sdhci-pci",
190 s
->pci
.bus
= qpci_init_pc(global_qtest
, NULL
);
192 /* Find PCI device and verify it's the right one */
193 s
->pci
.dev
= qpci_device_find(s
->pci
.bus
, QPCI_DEVFN(4, 0));
194 g_assert_nonnull(s
->pci
.dev
);
195 vendor_id
= qpci_config_readw(s
->pci
.dev
, PCI_VENDOR_ID
);
196 device_id
= qpci_config_readw(s
->pci
.dev
, PCI_DEVICE_ID
);
197 g_assert(vendor_id
== test
->pci
.vendor_id
);
198 g_assert(device_id
== test
->pci
.device_id
);
199 s
->mem_bar
= qpci_iomap(s
->pci
.dev
, 0, &barsize
);
200 qpci_device_enable(s
->pci
.dev
);
203 global_qtest
= qtest_startf("-machine %s", test
->machine
);
204 s
->addr
= test
->sdhci
.addr
;
210 static void machine_stop(QSDHCI
*s
)
213 qtest_quit(global_qtest
);
216 static void test_machine(const void *data
)
218 const struct sdhci_t
*test
= data
;
221 s
= machine_start(test
);
223 check_specs_version(s
, test
->sdhci
.version
);
224 check_capab_capareg(s
, test
->sdhci
.capab
.reg
);
225 check_capab_readonly(s
);
226 check_capab_v3(s
, test
->sdhci
.version
);
227 check_capab_sdma(s
, test
->sdhci
.capab
.sdma
);
228 check_capab_baseclock(s
, test
->sdhci
.baseclock
);
233 int main(int argc
, char *argv
[])
235 const char *arch
= qtest_get_arch();
239 g_test_init(&argc
, &argv
, NULL
);
240 for (i
= 0; i
< ARRAY_SIZE(models
); i
++) {
241 if (strcmp(arch
, models
[i
].arch
)) {
244 name
= g_strdup_printf("sdhci/%s", models
[i
].machine
);
245 qtest_add_data_func(name
, &models
[i
], test_machine
);