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 170c78f51eSPhilippe Mathieu-Daudé FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8); /* since v2 */ 18bc13038fSPhilippe Mathieu-Daudé FIELD(SDHC_CAPAB, SDMA, 22, 1); 19*f18e6d50SPhilippe Mathieu-Daudé FIELD(SDHC_CAPAB, SDR, 32, 3); /* since v3 */ 20*f18e6d50SPhilippe Mathieu-Daudé FIELD(SDHC_CAPAB, DRIVER, 36, 3); /* since v3 */ 218640cc11SPhilippe Mathieu-Daudé #define SDHC_HCVER 0xFE 228640cc11SPhilippe Mathieu-Daudé 238640cc11SPhilippe Mathieu-Daudé static const struct sdhci_t { 248640cc11SPhilippe Mathieu-Daudé const char *arch, *machine; 258640cc11SPhilippe Mathieu-Daudé struct { 268640cc11SPhilippe Mathieu-Daudé uintptr_t addr; 278640cc11SPhilippe Mathieu-Daudé uint8_t version; 288640cc11SPhilippe Mathieu-Daudé uint8_t baseclock; 298640cc11SPhilippe Mathieu-Daudé struct { 308640cc11SPhilippe Mathieu-Daudé bool sdma; 318640cc11SPhilippe Mathieu-Daudé uint64_t reg; 328640cc11SPhilippe Mathieu-Daudé } capab; 338640cc11SPhilippe Mathieu-Daudé } sdhci; 348640cc11SPhilippe Mathieu-Daudé struct { 358640cc11SPhilippe Mathieu-Daudé uint16_t vendor_id, device_id; 368640cc11SPhilippe Mathieu-Daudé } pci; 378640cc11SPhilippe Mathieu-Daudé } models[] = { 388640cc11SPhilippe Mathieu-Daudé /* PC via PCI */ 398640cc11SPhilippe Mathieu-Daudé { "x86_64", "pc", 408640cc11SPhilippe Mathieu-Daudé {-1, 2, 0, {1, 0x057834b4} }, 418640cc11SPhilippe Mathieu-Daudé .pci = { PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_SDHCI } }, 428640cc11SPhilippe Mathieu-Daudé 438640cc11SPhilippe Mathieu-Daudé /* Exynos4210 */ 448640cc11SPhilippe Mathieu-Daudé { "arm", "smdkc210", 458640cc11SPhilippe Mathieu-Daudé {0x12510000, 2, 0, {1, 0x5e80080} } }, 4627a49d3bSPhilippe Mathieu-Daudé 4709b9428dSPhilippe Mathieu-Daudé /* i.MX 6 */ 4809b9428dSPhilippe Mathieu-Daudé { "arm", "sabrelite", 4909b9428dSPhilippe Mathieu-Daudé {0x02190000, 3, 0, {1, 0x057834b4} } }, 5009b9428dSPhilippe Mathieu-Daudé 5109b9428dSPhilippe Mathieu-Daudé /* BCM2835 */ 5209b9428dSPhilippe Mathieu-Daudé { "arm", "raspi2", 5309b9428dSPhilippe Mathieu-Daudé {0x3f300000, 3, 52, {0, 0x052134b4} } }, 5409b9428dSPhilippe Mathieu-Daudé 5527a49d3bSPhilippe Mathieu-Daudé /* Zynq-7000 */ 5627a49d3bSPhilippe Mathieu-Daudé { "arm", "xilinx-zynq-a9", /* Datasheet: UG585 (v1.12.1) */ 5727a49d3bSPhilippe Mathieu-Daudé {0xe0100000, 2, 0, {1, 0x69ec0080} } }, 5827a49d3bSPhilippe Mathieu-Daudé 5909b9428dSPhilippe Mathieu-Daudé /* ZynqMP */ 6009b9428dSPhilippe Mathieu-Daudé { "aarch64", "xlnx-zcu102", /* Datasheet: UG1085 (v1.7) */ 6109b9428dSPhilippe Mathieu-Daudé {0xff160000, 3, 0, {1, 0x280737ec6481} } }, 6209b9428dSPhilippe Mathieu-Daudé 638640cc11SPhilippe Mathieu-Daudé }; 648640cc11SPhilippe Mathieu-Daudé 658640cc11SPhilippe Mathieu-Daudé typedef struct QSDHCI { 668640cc11SPhilippe Mathieu-Daudé struct { 678640cc11SPhilippe Mathieu-Daudé QPCIBus *bus; 688640cc11SPhilippe Mathieu-Daudé QPCIDevice *dev; 698640cc11SPhilippe Mathieu-Daudé } pci; 708640cc11SPhilippe Mathieu-Daudé union { 718640cc11SPhilippe Mathieu-Daudé QPCIBar mem_bar; 728640cc11SPhilippe Mathieu-Daudé uint64_t addr; 738640cc11SPhilippe Mathieu-Daudé }; 748640cc11SPhilippe Mathieu-Daudé } QSDHCI; 758640cc11SPhilippe Mathieu-Daudé 76efe9d524SPhilippe Mathieu-Daudé static uint16_t sdhci_readw(QSDHCI *s, uint32_t reg) 77efe9d524SPhilippe Mathieu-Daudé { 78efe9d524SPhilippe Mathieu-Daudé uint16_t val; 79efe9d524SPhilippe Mathieu-Daudé 80efe9d524SPhilippe Mathieu-Daudé if (s->pci.dev) { 81efe9d524SPhilippe Mathieu-Daudé val = qpci_io_readw(s->pci.dev, s->mem_bar, reg); 82efe9d524SPhilippe Mathieu-Daudé } else { 83efe9d524SPhilippe Mathieu-Daudé val = qtest_readw(global_qtest, s->addr + reg); 84efe9d524SPhilippe Mathieu-Daudé } 85efe9d524SPhilippe Mathieu-Daudé 86efe9d524SPhilippe Mathieu-Daudé return val; 87efe9d524SPhilippe Mathieu-Daudé } 88efe9d524SPhilippe Mathieu-Daudé 898640cc11SPhilippe Mathieu-Daudé static uint64_t sdhci_readq(QSDHCI *s, uint32_t reg) 908640cc11SPhilippe Mathieu-Daudé { 918640cc11SPhilippe Mathieu-Daudé uint64_t val; 928640cc11SPhilippe Mathieu-Daudé 938640cc11SPhilippe Mathieu-Daudé if (s->pci.dev) { 948640cc11SPhilippe Mathieu-Daudé val = qpci_io_readq(s->pci.dev, s->mem_bar, reg); 958640cc11SPhilippe Mathieu-Daudé } else { 968640cc11SPhilippe Mathieu-Daudé val = qtest_readq(global_qtest, s->addr + reg); 978640cc11SPhilippe Mathieu-Daudé } 988640cc11SPhilippe Mathieu-Daudé 998640cc11SPhilippe Mathieu-Daudé return val; 1008640cc11SPhilippe Mathieu-Daudé } 1018640cc11SPhilippe Mathieu-Daudé 102556f9acaSPhilippe Mathieu-Daudé static void sdhci_writeq(QSDHCI *s, uint32_t reg, uint64_t val) 103556f9acaSPhilippe Mathieu-Daudé { 104556f9acaSPhilippe Mathieu-Daudé if (s->pci.dev) { 105556f9acaSPhilippe Mathieu-Daudé qpci_io_writeq(s->pci.dev, s->mem_bar, reg, val); 106556f9acaSPhilippe Mathieu-Daudé } else { 107556f9acaSPhilippe Mathieu-Daudé qtest_writeq(global_qtest, s->addr + reg, val); 108556f9acaSPhilippe Mathieu-Daudé } 109556f9acaSPhilippe Mathieu-Daudé } 110556f9acaSPhilippe Mathieu-Daudé 111efe9d524SPhilippe Mathieu-Daudé static void check_specs_version(QSDHCI *s, uint8_t version) 112efe9d524SPhilippe Mathieu-Daudé { 113efe9d524SPhilippe Mathieu-Daudé uint32_t v; 114efe9d524SPhilippe Mathieu-Daudé 115efe9d524SPhilippe Mathieu-Daudé v = sdhci_readw(s, SDHC_HCVER); 116efe9d524SPhilippe Mathieu-Daudé v &= 0xff; 117efe9d524SPhilippe Mathieu-Daudé v += 1; 118efe9d524SPhilippe Mathieu-Daudé g_assert_cmpuint(v, ==, version); 119efe9d524SPhilippe Mathieu-Daudé } 120efe9d524SPhilippe Mathieu-Daudé 1218640cc11SPhilippe Mathieu-Daudé static void check_capab_capareg(QSDHCI *s, uint64_t expec_capab) 1228640cc11SPhilippe Mathieu-Daudé { 1238640cc11SPhilippe Mathieu-Daudé uint64_t capab; 1248640cc11SPhilippe Mathieu-Daudé 1258640cc11SPhilippe Mathieu-Daudé capab = sdhci_readq(s, SDHC_CAPAB); 1268640cc11SPhilippe Mathieu-Daudé g_assert_cmphex(capab, ==, expec_capab); 1278640cc11SPhilippe Mathieu-Daudé } 1288640cc11SPhilippe Mathieu-Daudé 129556f9acaSPhilippe Mathieu-Daudé static void check_capab_readonly(QSDHCI *s) 130556f9acaSPhilippe Mathieu-Daudé { 131556f9acaSPhilippe Mathieu-Daudé const uint64_t vrand = 0x123456789abcdef; 132556f9acaSPhilippe Mathieu-Daudé uint64_t capab0, capab1; 133556f9acaSPhilippe Mathieu-Daudé 134556f9acaSPhilippe Mathieu-Daudé capab0 = sdhci_readq(s, SDHC_CAPAB); 135556f9acaSPhilippe Mathieu-Daudé g_assert_cmpuint(capab0, !=, vrand); 136556f9acaSPhilippe Mathieu-Daudé 137556f9acaSPhilippe Mathieu-Daudé sdhci_writeq(s, SDHC_CAPAB, vrand); 138556f9acaSPhilippe Mathieu-Daudé capab1 = sdhci_readq(s, SDHC_CAPAB); 139556f9acaSPhilippe Mathieu-Daudé g_assert_cmpuint(capab1, !=, vrand); 140556f9acaSPhilippe Mathieu-Daudé g_assert_cmpuint(capab1, ==, capab0); 141556f9acaSPhilippe Mathieu-Daudé } 142556f9acaSPhilippe Mathieu-Daudé 1430c78f51eSPhilippe Mathieu-Daudé static void check_capab_baseclock(QSDHCI *s, uint8_t expec_freq) 1440c78f51eSPhilippe Mathieu-Daudé { 1450c78f51eSPhilippe Mathieu-Daudé uint64_t capab, capab_freq; 1460c78f51eSPhilippe Mathieu-Daudé 1470c78f51eSPhilippe Mathieu-Daudé if (!expec_freq) { 1480c78f51eSPhilippe Mathieu-Daudé return; 1490c78f51eSPhilippe Mathieu-Daudé } 1500c78f51eSPhilippe Mathieu-Daudé capab = sdhci_readq(s, SDHC_CAPAB); 1510c78f51eSPhilippe Mathieu-Daudé capab_freq = FIELD_EX64(capab, SDHC_CAPAB, BASECLKFREQ); 1520c78f51eSPhilippe Mathieu-Daudé g_assert_cmpuint(capab_freq, ==, expec_freq); 1530c78f51eSPhilippe Mathieu-Daudé } 1540c78f51eSPhilippe Mathieu-Daudé 155bc13038fSPhilippe Mathieu-Daudé static void check_capab_sdma(QSDHCI *s, bool supported) 156bc13038fSPhilippe Mathieu-Daudé { 157bc13038fSPhilippe Mathieu-Daudé uint64_t capab, capab_sdma; 158bc13038fSPhilippe Mathieu-Daudé 159bc13038fSPhilippe Mathieu-Daudé capab = sdhci_readq(s, SDHC_CAPAB); 160bc13038fSPhilippe Mathieu-Daudé capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA); 161bc13038fSPhilippe Mathieu-Daudé g_assert_cmpuint(capab_sdma, ==, supported); 162bc13038fSPhilippe Mathieu-Daudé } 163bc13038fSPhilippe Mathieu-Daudé 164*f18e6d50SPhilippe Mathieu-Daudé static void check_capab_v3(QSDHCI *s, uint8_t version) 165*f18e6d50SPhilippe Mathieu-Daudé { 166*f18e6d50SPhilippe Mathieu-Daudé uint64_t capab, capab_v3; 167*f18e6d50SPhilippe Mathieu-Daudé 168*f18e6d50SPhilippe Mathieu-Daudé if (version < 3) { 169*f18e6d50SPhilippe Mathieu-Daudé /* before v3 those fields are RESERVED */ 170*f18e6d50SPhilippe Mathieu-Daudé capab = sdhci_readq(s, SDHC_CAPAB); 171*f18e6d50SPhilippe Mathieu-Daudé capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, SDR); 172*f18e6d50SPhilippe Mathieu-Daudé g_assert_cmpuint(capab_v3, ==, 0); 173*f18e6d50SPhilippe Mathieu-Daudé capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, DRIVER); 174*f18e6d50SPhilippe Mathieu-Daudé g_assert_cmpuint(capab_v3, ==, 0); 175*f18e6d50SPhilippe Mathieu-Daudé } 176*f18e6d50SPhilippe Mathieu-Daudé } 177*f18e6d50SPhilippe Mathieu-Daudé 1788640cc11SPhilippe Mathieu-Daudé static QSDHCI *machine_start(const struct sdhci_t *test) 1798640cc11SPhilippe Mathieu-Daudé { 1808640cc11SPhilippe Mathieu-Daudé QSDHCI *s = g_new0(QSDHCI, 1); 1818640cc11SPhilippe Mathieu-Daudé 1828640cc11SPhilippe Mathieu-Daudé if (test->pci.vendor_id) { 1838640cc11SPhilippe Mathieu-Daudé /* PCI */ 1848640cc11SPhilippe Mathieu-Daudé uint16_t vendor_id, device_id; 1858640cc11SPhilippe Mathieu-Daudé uint64_t barsize; 1868640cc11SPhilippe Mathieu-Daudé 1878640cc11SPhilippe Mathieu-Daudé global_qtest = qtest_startf("-machine %s -device sdhci-pci", 1888640cc11SPhilippe Mathieu-Daudé test->machine); 1898640cc11SPhilippe Mathieu-Daudé 1908640cc11SPhilippe Mathieu-Daudé s->pci.bus = qpci_init_pc(NULL); 1918640cc11SPhilippe Mathieu-Daudé 1928640cc11SPhilippe Mathieu-Daudé /* Find PCI device and verify it's the right one */ 1938640cc11SPhilippe Mathieu-Daudé s->pci.dev = qpci_device_find(s->pci.bus, QPCI_DEVFN(4, 0)); 1948640cc11SPhilippe Mathieu-Daudé g_assert_nonnull(s->pci.dev); 1958640cc11SPhilippe Mathieu-Daudé vendor_id = qpci_config_readw(s->pci.dev, PCI_VENDOR_ID); 1968640cc11SPhilippe Mathieu-Daudé device_id = qpci_config_readw(s->pci.dev, PCI_DEVICE_ID); 1978640cc11SPhilippe Mathieu-Daudé g_assert(vendor_id == test->pci.vendor_id); 1988640cc11SPhilippe Mathieu-Daudé g_assert(device_id == test->pci.device_id); 1998640cc11SPhilippe Mathieu-Daudé s->mem_bar = qpci_iomap(s->pci.dev, 0, &barsize); 2008640cc11SPhilippe Mathieu-Daudé qpci_device_enable(s->pci.dev); 2018640cc11SPhilippe Mathieu-Daudé } else { 2028640cc11SPhilippe Mathieu-Daudé /* SysBus */ 2038640cc11SPhilippe Mathieu-Daudé global_qtest = qtest_startf("-machine %s", test->machine); 2048640cc11SPhilippe Mathieu-Daudé s->addr = test->sdhci.addr; 2058640cc11SPhilippe Mathieu-Daudé } 2068640cc11SPhilippe Mathieu-Daudé 2078640cc11SPhilippe Mathieu-Daudé return s; 2088640cc11SPhilippe Mathieu-Daudé } 2098640cc11SPhilippe Mathieu-Daudé 2108640cc11SPhilippe Mathieu-Daudé static void machine_stop(QSDHCI *s) 2118640cc11SPhilippe Mathieu-Daudé { 2128640cc11SPhilippe Mathieu-Daudé g_free(s->pci.dev); 2138640cc11SPhilippe Mathieu-Daudé qtest_quit(global_qtest); 2148640cc11SPhilippe Mathieu-Daudé } 2158640cc11SPhilippe Mathieu-Daudé 2168640cc11SPhilippe Mathieu-Daudé static void test_machine(const void *data) 2178640cc11SPhilippe Mathieu-Daudé { 2188640cc11SPhilippe Mathieu-Daudé const struct sdhci_t *test = data; 2198640cc11SPhilippe Mathieu-Daudé QSDHCI *s; 2208640cc11SPhilippe Mathieu-Daudé 2218640cc11SPhilippe Mathieu-Daudé s = machine_start(test); 2228640cc11SPhilippe Mathieu-Daudé 223efe9d524SPhilippe Mathieu-Daudé check_specs_version(s, test->sdhci.version); 2248640cc11SPhilippe Mathieu-Daudé check_capab_capareg(s, test->sdhci.capab.reg); 225556f9acaSPhilippe Mathieu-Daudé check_capab_readonly(s); 226*f18e6d50SPhilippe Mathieu-Daudé check_capab_v3(s, test->sdhci.version); 227bc13038fSPhilippe Mathieu-Daudé check_capab_sdma(s, test->sdhci.capab.sdma); 2280c78f51eSPhilippe Mathieu-Daudé check_capab_baseclock(s, test->sdhci.baseclock); 2298640cc11SPhilippe Mathieu-Daudé 2308640cc11SPhilippe Mathieu-Daudé machine_stop(s); 2318640cc11SPhilippe Mathieu-Daudé } 2328640cc11SPhilippe Mathieu-Daudé 2338640cc11SPhilippe Mathieu-Daudé int main(int argc, char *argv[]) 2348640cc11SPhilippe Mathieu-Daudé { 2358640cc11SPhilippe Mathieu-Daudé const char *arch = qtest_get_arch(); 2368640cc11SPhilippe Mathieu-Daudé char *name; 2378640cc11SPhilippe Mathieu-Daudé int i; 2388640cc11SPhilippe Mathieu-Daudé 2398640cc11SPhilippe Mathieu-Daudé g_test_init(&argc, &argv, NULL); 2408640cc11SPhilippe Mathieu-Daudé for (i = 0; i < ARRAY_SIZE(models); i++) { 2418640cc11SPhilippe Mathieu-Daudé if (strcmp(arch, models[i].arch)) { 2428640cc11SPhilippe Mathieu-Daudé continue; 2438640cc11SPhilippe Mathieu-Daudé } 2448640cc11SPhilippe Mathieu-Daudé name = g_strdup_printf("sdhci/%s", models[i].machine); 2458640cc11SPhilippe Mathieu-Daudé qtest_add_data_func(name, &models[i], test_machine); 2468640cc11SPhilippe Mathieu-Daudé g_free(name); 2478640cc11SPhilippe Mathieu-Daudé } 2488640cc11SPhilippe Mathieu-Daudé 2498640cc11SPhilippe Mathieu-Daudé return g_test_run(); 2508640cc11SPhilippe Mathieu-Daudé } 251