18640cc11SPhilippe Mathieu-Daudé /* 28640cc11SPhilippe Mathieu-Daudé * QTest testcase for SDHCI controllers 38640cc11SPhilippe Mathieu-Daudé * 48640cc11SPhilippe Mathieu-Daudé * Written by Philippe Mathieu-Daudé <f4bug@amsat.org> 58640cc11SPhilippe Mathieu-Daudé * 68640cc11SPhilippe Mathieu-Daudé * This work is licensed under the terms of the GNU GPL, version 2 or later. 78640cc11SPhilippe Mathieu-Daudé * See the COPYING file in the top-level directory. 88640cc11SPhilippe Mathieu-Daudé * SPDX-License-Identifier: GPL-2.0-or-later 98640cc11SPhilippe Mathieu-Daudé */ 108640cc11SPhilippe Mathieu-Daudé #include "qemu/osdep.h" 118640cc11SPhilippe Mathieu-Daudé #include "hw/registerfields.h" 128640cc11SPhilippe Mathieu-Daudé #include "libqtest.h" 138640cc11SPhilippe Mathieu-Daudé #include "libqos/pci-pc.h" 148640cc11SPhilippe Mathieu-Daudé #include "hw/pci/pci.h" 158640cc11SPhilippe Mathieu-Daudé 168640cc11SPhilippe Mathieu-Daudé #define SDHC_CAPAB 0x40 17*0c78f51eSPhilippe Mathieu-Daudé FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8); /* since v2 */ 188640cc11SPhilippe Mathieu-Daudé #define SDHC_HCVER 0xFE 198640cc11SPhilippe Mathieu-Daudé 208640cc11SPhilippe Mathieu-Daudé static const struct sdhci_t { 218640cc11SPhilippe Mathieu-Daudé const char *arch, *machine; 228640cc11SPhilippe Mathieu-Daudé struct { 238640cc11SPhilippe Mathieu-Daudé uintptr_t addr; 248640cc11SPhilippe Mathieu-Daudé uint8_t version; 258640cc11SPhilippe Mathieu-Daudé uint8_t baseclock; 268640cc11SPhilippe Mathieu-Daudé struct { 278640cc11SPhilippe Mathieu-Daudé bool sdma; 288640cc11SPhilippe Mathieu-Daudé uint64_t reg; 298640cc11SPhilippe Mathieu-Daudé } capab; 308640cc11SPhilippe Mathieu-Daudé } sdhci; 318640cc11SPhilippe Mathieu-Daudé struct { 328640cc11SPhilippe Mathieu-Daudé uint16_t vendor_id, device_id; 338640cc11SPhilippe Mathieu-Daudé } pci; 348640cc11SPhilippe Mathieu-Daudé } models[] = { 358640cc11SPhilippe Mathieu-Daudé /* PC via PCI */ 368640cc11SPhilippe Mathieu-Daudé { "x86_64", "pc", 378640cc11SPhilippe Mathieu-Daudé {-1, 2, 0, {1, 0x057834b4} }, 388640cc11SPhilippe Mathieu-Daudé .pci = { PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_SDHCI } }, 398640cc11SPhilippe Mathieu-Daudé 408640cc11SPhilippe Mathieu-Daudé /* Exynos4210 */ 418640cc11SPhilippe Mathieu-Daudé { "arm", "smdkc210", 428640cc11SPhilippe Mathieu-Daudé {0x12510000, 2, 0, {1, 0x5e80080} } }, 438640cc11SPhilippe Mathieu-Daudé }; 448640cc11SPhilippe Mathieu-Daudé 458640cc11SPhilippe Mathieu-Daudé typedef struct QSDHCI { 468640cc11SPhilippe Mathieu-Daudé struct { 478640cc11SPhilippe Mathieu-Daudé QPCIBus *bus; 488640cc11SPhilippe Mathieu-Daudé QPCIDevice *dev; 498640cc11SPhilippe Mathieu-Daudé } pci; 508640cc11SPhilippe Mathieu-Daudé union { 518640cc11SPhilippe Mathieu-Daudé QPCIBar mem_bar; 528640cc11SPhilippe Mathieu-Daudé uint64_t addr; 538640cc11SPhilippe Mathieu-Daudé }; 548640cc11SPhilippe Mathieu-Daudé } QSDHCI; 558640cc11SPhilippe Mathieu-Daudé 568640cc11SPhilippe Mathieu-Daudé static uint64_t sdhci_readq(QSDHCI *s, uint32_t reg) 578640cc11SPhilippe Mathieu-Daudé { 588640cc11SPhilippe Mathieu-Daudé uint64_t val; 598640cc11SPhilippe Mathieu-Daudé 608640cc11SPhilippe Mathieu-Daudé if (s->pci.dev) { 618640cc11SPhilippe Mathieu-Daudé val = qpci_io_readq(s->pci.dev, s->mem_bar, reg); 628640cc11SPhilippe Mathieu-Daudé } else { 638640cc11SPhilippe Mathieu-Daudé val = qtest_readq(global_qtest, s->addr + reg); 648640cc11SPhilippe Mathieu-Daudé } 658640cc11SPhilippe Mathieu-Daudé 668640cc11SPhilippe Mathieu-Daudé return val; 678640cc11SPhilippe Mathieu-Daudé } 688640cc11SPhilippe Mathieu-Daudé 69556f9acaSPhilippe Mathieu-Daudé static void sdhci_writeq(QSDHCI *s, uint32_t reg, uint64_t val) 70556f9acaSPhilippe Mathieu-Daudé { 71556f9acaSPhilippe Mathieu-Daudé if (s->pci.dev) { 72556f9acaSPhilippe Mathieu-Daudé qpci_io_writeq(s->pci.dev, s->mem_bar, reg, val); 73556f9acaSPhilippe Mathieu-Daudé } else { 74556f9acaSPhilippe Mathieu-Daudé qtest_writeq(global_qtest, s->addr + reg, val); 75556f9acaSPhilippe Mathieu-Daudé } 76556f9acaSPhilippe Mathieu-Daudé } 77556f9acaSPhilippe Mathieu-Daudé 788640cc11SPhilippe Mathieu-Daudé static void check_capab_capareg(QSDHCI *s, uint64_t expec_capab) 798640cc11SPhilippe Mathieu-Daudé { 808640cc11SPhilippe Mathieu-Daudé uint64_t capab; 818640cc11SPhilippe Mathieu-Daudé 828640cc11SPhilippe Mathieu-Daudé capab = sdhci_readq(s, SDHC_CAPAB); 838640cc11SPhilippe Mathieu-Daudé g_assert_cmphex(capab, ==, expec_capab); 848640cc11SPhilippe Mathieu-Daudé } 858640cc11SPhilippe Mathieu-Daudé 86556f9acaSPhilippe Mathieu-Daudé static void check_capab_readonly(QSDHCI *s) 87556f9acaSPhilippe Mathieu-Daudé { 88556f9acaSPhilippe Mathieu-Daudé const uint64_t vrand = 0x123456789abcdef; 89556f9acaSPhilippe Mathieu-Daudé uint64_t capab0, capab1; 90556f9acaSPhilippe Mathieu-Daudé 91556f9acaSPhilippe Mathieu-Daudé capab0 = sdhci_readq(s, SDHC_CAPAB); 92556f9acaSPhilippe Mathieu-Daudé g_assert_cmpuint(capab0, !=, vrand); 93556f9acaSPhilippe Mathieu-Daudé 94556f9acaSPhilippe Mathieu-Daudé sdhci_writeq(s, SDHC_CAPAB, vrand); 95556f9acaSPhilippe Mathieu-Daudé capab1 = sdhci_readq(s, SDHC_CAPAB); 96556f9acaSPhilippe Mathieu-Daudé g_assert_cmpuint(capab1, !=, vrand); 97556f9acaSPhilippe Mathieu-Daudé g_assert_cmpuint(capab1, ==, capab0); 98556f9acaSPhilippe Mathieu-Daudé } 99556f9acaSPhilippe Mathieu-Daudé 100*0c78f51eSPhilippe Mathieu-Daudé static void check_capab_baseclock(QSDHCI *s, uint8_t expec_freq) 101*0c78f51eSPhilippe Mathieu-Daudé { 102*0c78f51eSPhilippe Mathieu-Daudé uint64_t capab, capab_freq; 103*0c78f51eSPhilippe Mathieu-Daudé 104*0c78f51eSPhilippe Mathieu-Daudé if (!expec_freq) { 105*0c78f51eSPhilippe Mathieu-Daudé return; 106*0c78f51eSPhilippe Mathieu-Daudé } 107*0c78f51eSPhilippe Mathieu-Daudé capab = sdhci_readq(s, SDHC_CAPAB); 108*0c78f51eSPhilippe Mathieu-Daudé capab_freq = FIELD_EX64(capab, SDHC_CAPAB, BASECLKFREQ); 109*0c78f51eSPhilippe Mathieu-Daudé g_assert_cmpuint(capab_freq, ==, expec_freq); 110*0c78f51eSPhilippe Mathieu-Daudé } 111*0c78f51eSPhilippe Mathieu-Daudé 1128640cc11SPhilippe Mathieu-Daudé static QSDHCI *machine_start(const struct sdhci_t *test) 1138640cc11SPhilippe Mathieu-Daudé { 1148640cc11SPhilippe Mathieu-Daudé QSDHCI *s = g_new0(QSDHCI, 1); 1158640cc11SPhilippe Mathieu-Daudé 1168640cc11SPhilippe Mathieu-Daudé if (test->pci.vendor_id) { 1178640cc11SPhilippe Mathieu-Daudé /* PCI */ 1188640cc11SPhilippe Mathieu-Daudé uint16_t vendor_id, device_id; 1198640cc11SPhilippe Mathieu-Daudé uint64_t barsize; 1208640cc11SPhilippe Mathieu-Daudé 1218640cc11SPhilippe Mathieu-Daudé global_qtest = qtest_startf("-machine %s -device sdhci-pci", 1228640cc11SPhilippe Mathieu-Daudé test->machine); 1238640cc11SPhilippe Mathieu-Daudé 1248640cc11SPhilippe Mathieu-Daudé s->pci.bus = qpci_init_pc(NULL); 1258640cc11SPhilippe Mathieu-Daudé 1268640cc11SPhilippe Mathieu-Daudé /* Find PCI device and verify it's the right one */ 1278640cc11SPhilippe Mathieu-Daudé s->pci.dev = qpci_device_find(s->pci.bus, QPCI_DEVFN(4, 0)); 1288640cc11SPhilippe Mathieu-Daudé g_assert_nonnull(s->pci.dev); 1298640cc11SPhilippe Mathieu-Daudé vendor_id = qpci_config_readw(s->pci.dev, PCI_VENDOR_ID); 1308640cc11SPhilippe Mathieu-Daudé device_id = qpci_config_readw(s->pci.dev, PCI_DEVICE_ID); 1318640cc11SPhilippe Mathieu-Daudé g_assert(vendor_id == test->pci.vendor_id); 1328640cc11SPhilippe Mathieu-Daudé g_assert(device_id == test->pci.device_id); 1338640cc11SPhilippe Mathieu-Daudé s->mem_bar = qpci_iomap(s->pci.dev, 0, &barsize); 1348640cc11SPhilippe Mathieu-Daudé qpci_device_enable(s->pci.dev); 1358640cc11SPhilippe Mathieu-Daudé } else { 1368640cc11SPhilippe Mathieu-Daudé /* SysBus */ 1378640cc11SPhilippe Mathieu-Daudé global_qtest = qtest_startf("-machine %s", test->machine); 1388640cc11SPhilippe Mathieu-Daudé s->addr = test->sdhci.addr; 1398640cc11SPhilippe Mathieu-Daudé } 1408640cc11SPhilippe Mathieu-Daudé 1418640cc11SPhilippe Mathieu-Daudé return s; 1428640cc11SPhilippe Mathieu-Daudé } 1438640cc11SPhilippe Mathieu-Daudé 1448640cc11SPhilippe Mathieu-Daudé static void machine_stop(QSDHCI *s) 1458640cc11SPhilippe Mathieu-Daudé { 1468640cc11SPhilippe Mathieu-Daudé g_free(s->pci.dev); 1478640cc11SPhilippe Mathieu-Daudé qtest_quit(global_qtest); 1488640cc11SPhilippe Mathieu-Daudé } 1498640cc11SPhilippe Mathieu-Daudé 1508640cc11SPhilippe Mathieu-Daudé static void test_machine(const void *data) 1518640cc11SPhilippe Mathieu-Daudé { 1528640cc11SPhilippe Mathieu-Daudé const struct sdhci_t *test = data; 1538640cc11SPhilippe Mathieu-Daudé QSDHCI *s; 1548640cc11SPhilippe Mathieu-Daudé 1558640cc11SPhilippe Mathieu-Daudé s = machine_start(test); 1568640cc11SPhilippe Mathieu-Daudé 1578640cc11SPhilippe Mathieu-Daudé check_capab_capareg(s, test->sdhci.capab.reg); 158556f9acaSPhilippe Mathieu-Daudé check_capab_readonly(s); 159*0c78f51eSPhilippe Mathieu-Daudé check_capab_baseclock(s, test->sdhci.baseclock); 1608640cc11SPhilippe Mathieu-Daudé 1618640cc11SPhilippe Mathieu-Daudé machine_stop(s); 1628640cc11SPhilippe Mathieu-Daudé } 1638640cc11SPhilippe Mathieu-Daudé 1648640cc11SPhilippe Mathieu-Daudé int main(int argc, char *argv[]) 1658640cc11SPhilippe Mathieu-Daudé { 1668640cc11SPhilippe Mathieu-Daudé const char *arch = qtest_get_arch(); 1678640cc11SPhilippe Mathieu-Daudé char *name; 1688640cc11SPhilippe Mathieu-Daudé int i; 1698640cc11SPhilippe Mathieu-Daudé 1708640cc11SPhilippe Mathieu-Daudé g_test_init(&argc, &argv, NULL); 1718640cc11SPhilippe Mathieu-Daudé for (i = 0; i < ARRAY_SIZE(models); i++) { 1728640cc11SPhilippe Mathieu-Daudé if (strcmp(arch, models[i].arch)) { 1738640cc11SPhilippe Mathieu-Daudé continue; 1748640cc11SPhilippe Mathieu-Daudé } 1758640cc11SPhilippe Mathieu-Daudé name = g_strdup_printf("sdhci/%s", models[i].machine); 1768640cc11SPhilippe Mathieu-Daudé qtest_add_data_func(name, &models[i], test_machine); 1778640cc11SPhilippe Mathieu-Daudé g_free(name); 1788640cc11SPhilippe Mathieu-Daudé } 1798640cc11SPhilippe Mathieu-Daudé 1808640cc11SPhilippe Mathieu-Daudé return g_test_run(); 1818640cc11SPhilippe Mathieu-Daudé } 182