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 #define SDHC_HCVER 0xFE
21 static const struct sdhci_t
{
22 const char *arch
, *machine
;
33 uint16_t vendor_id
, device_id
;
38 {-1, 2, 0, {1, 0x057834b4} },
39 .pci
= { PCI_VENDOR_ID_REDHAT
, PCI_DEVICE_ID_REDHAT_SDHCI
} },
43 {0x12510000, 2, 0, {1, 0x5e80080} } },
46 { "arm", "xilinx-zynq-a9", /* Datasheet: UG585 (v1.12.1) */
47 {0xe0100000, 2, 0, {1, 0x69ec0080} } },
51 typedef struct QSDHCI
{
62 static uint16_t sdhci_readw(QSDHCI
*s
, uint32_t reg
)
67 val
= qpci_io_readw(s
->pci
.dev
, s
->mem_bar
, reg
);
69 val
= qtest_readw(global_qtest
, s
->addr
+ reg
);
75 static uint64_t sdhci_readq(QSDHCI
*s
, uint32_t reg
)
80 val
= qpci_io_readq(s
->pci
.dev
, s
->mem_bar
, reg
);
82 val
= qtest_readq(global_qtest
, s
->addr
+ reg
);
88 static void sdhci_writeq(QSDHCI
*s
, uint32_t reg
, uint64_t val
)
91 qpci_io_writeq(s
->pci
.dev
, s
->mem_bar
, reg
, val
);
93 qtest_writeq(global_qtest
, s
->addr
+ reg
, val
);
97 static void check_specs_version(QSDHCI
*s
, uint8_t version
)
101 v
= sdhci_readw(s
, SDHC_HCVER
);
104 g_assert_cmpuint(v
, ==, version
);
107 static void check_capab_capareg(QSDHCI
*s
, uint64_t expec_capab
)
111 capab
= sdhci_readq(s
, SDHC_CAPAB
);
112 g_assert_cmphex(capab
, ==, expec_capab
);
115 static void check_capab_readonly(QSDHCI
*s
)
117 const uint64_t vrand
= 0x123456789abcdef;
118 uint64_t capab0
, capab1
;
120 capab0
= sdhci_readq(s
, SDHC_CAPAB
);
121 g_assert_cmpuint(capab0
, !=, vrand
);
123 sdhci_writeq(s
, SDHC_CAPAB
, vrand
);
124 capab1
= sdhci_readq(s
, SDHC_CAPAB
);
125 g_assert_cmpuint(capab1
, !=, vrand
);
126 g_assert_cmpuint(capab1
, ==, capab0
);
129 static void check_capab_baseclock(QSDHCI
*s
, uint8_t expec_freq
)
131 uint64_t capab
, capab_freq
;
136 capab
= sdhci_readq(s
, SDHC_CAPAB
);
137 capab_freq
= FIELD_EX64(capab
, SDHC_CAPAB
, BASECLKFREQ
);
138 g_assert_cmpuint(capab_freq
, ==, expec_freq
);
141 static void check_capab_sdma(QSDHCI
*s
, bool supported
)
143 uint64_t capab
, capab_sdma
;
145 capab
= sdhci_readq(s
, SDHC_CAPAB
);
146 capab_sdma
= FIELD_EX64(capab
, SDHC_CAPAB
, SDMA
);
147 g_assert_cmpuint(capab_sdma
, ==, supported
);
150 static QSDHCI
*machine_start(const struct sdhci_t
*test
)
152 QSDHCI
*s
= g_new0(QSDHCI
, 1);
154 if (test
->pci
.vendor_id
) {
156 uint16_t vendor_id
, device_id
;
159 global_qtest
= qtest_startf("-machine %s -device sdhci-pci",
162 s
->pci
.bus
= qpci_init_pc(NULL
);
164 /* Find PCI device and verify it's the right one */
165 s
->pci
.dev
= qpci_device_find(s
->pci
.bus
, QPCI_DEVFN(4, 0));
166 g_assert_nonnull(s
->pci
.dev
);
167 vendor_id
= qpci_config_readw(s
->pci
.dev
, PCI_VENDOR_ID
);
168 device_id
= qpci_config_readw(s
->pci
.dev
, PCI_DEVICE_ID
);
169 g_assert(vendor_id
== test
->pci
.vendor_id
);
170 g_assert(device_id
== test
->pci
.device_id
);
171 s
->mem_bar
= qpci_iomap(s
->pci
.dev
, 0, &barsize
);
172 qpci_device_enable(s
->pci
.dev
);
175 global_qtest
= qtest_startf("-machine %s", test
->machine
);
176 s
->addr
= test
->sdhci
.addr
;
182 static void machine_stop(QSDHCI
*s
)
185 qtest_quit(global_qtest
);
188 static void test_machine(const void *data
)
190 const struct sdhci_t
*test
= data
;
193 s
= machine_start(test
);
195 check_specs_version(s
, test
->sdhci
.version
);
196 check_capab_capareg(s
, test
->sdhci
.capab
.reg
);
197 check_capab_readonly(s
);
198 check_capab_sdma(s
, test
->sdhci
.capab
.sdma
);
199 check_capab_baseclock(s
, test
->sdhci
.baseclock
);
204 int main(int argc
, char *argv
[])
206 const char *arch
= qtest_get_arch();
210 g_test_init(&argc
, &argv
, NULL
);
211 for (i
= 0; i
< ARRAY_SIZE(models
); i
++) {
212 if (strcmp(arch
, models
[i
].arch
)) {
215 name
= g_strdup_printf("sdhci/%s", models
[i
].machine
);
216 qtest_add_data_func(name
, &models
[i
], test_machine
);