1 /* 2 * QTest testcase for SDHCI controllers 3 * 4 * Written by Philippe Mathieu-Daudé <f4bug@amsat.org> 5 * 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 9 */ 10 #include "qemu/osdep.h" 11 #include "hw/registerfields.h" 12 #include "libqtest.h" 13 #include "libqos/pci-pc.h" 14 #include "hw/pci/pci.h" 15 16 #define SDHC_CAPAB 0x40 17 #define SDHC_HCVER 0xFE 18 19 static const struct sdhci_t { 20 const char *arch, *machine; 21 struct { 22 uintptr_t addr; 23 uint8_t version; 24 uint8_t baseclock; 25 struct { 26 bool sdma; 27 uint64_t reg; 28 } capab; 29 } sdhci; 30 struct { 31 uint16_t vendor_id, device_id; 32 } pci; 33 } models[] = { 34 /* PC via PCI */ 35 { "x86_64", "pc", 36 {-1, 2, 0, {1, 0x057834b4} }, 37 .pci = { PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_SDHCI } }, 38 39 /* Exynos4210 */ 40 { "arm", "smdkc210", 41 {0x12510000, 2, 0, {1, 0x5e80080} } }, 42 }; 43 44 typedef struct QSDHCI { 45 struct { 46 QPCIBus *bus; 47 QPCIDevice *dev; 48 } pci; 49 union { 50 QPCIBar mem_bar; 51 uint64_t addr; 52 }; 53 } QSDHCI; 54 55 static uint64_t sdhci_readq(QSDHCI *s, uint32_t reg) 56 { 57 uint64_t val; 58 59 if (s->pci.dev) { 60 val = qpci_io_readq(s->pci.dev, s->mem_bar, reg); 61 } else { 62 val = qtest_readq(global_qtest, s->addr + reg); 63 } 64 65 return val; 66 } 67 68 static void sdhci_writeq(QSDHCI *s, uint32_t reg, uint64_t val) 69 { 70 if (s->pci.dev) { 71 qpci_io_writeq(s->pci.dev, s->mem_bar, reg, val); 72 } else { 73 qtest_writeq(global_qtest, s->addr + reg, val); 74 } 75 } 76 77 static void check_capab_capareg(QSDHCI *s, uint64_t expec_capab) 78 { 79 uint64_t capab; 80 81 capab = sdhci_readq(s, SDHC_CAPAB); 82 g_assert_cmphex(capab, ==, expec_capab); 83 } 84 85 static void check_capab_readonly(QSDHCI *s) 86 { 87 const uint64_t vrand = 0x123456789abcdef; 88 uint64_t capab0, capab1; 89 90 capab0 = sdhci_readq(s, SDHC_CAPAB); 91 g_assert_cmpuint(capab0, !=, vrand); 92 93 sdhci_writeq(s, SDHC_CAPAB, vrand); 94 capab1 = sdhci_readq(s, SDHC_CAPAB); 95 g_assert_cmpuint(capab1, !=, vrand); 96 g_assert_cmpuint(capab1, ==, capab0); 97 } 98 99 static QSDHCI *machine_start(const struct sdhci_t *test) 100 { 101 QSDHCI *s = g_new0(QSDHCI, 1); 102 103 if (test->pci.vendor_id) { 104 /* PCI */ 105 uint16_t vendor_id, device_id; 106 uint64_t barsize; 107 108 global_qtest = qtest_startf("-machine %s -device sdhci-pci", 109 test->machine); 110 111 s->pci.bus = qpci_init_pc(NULL); 112 113 /* Find PCI device and verify it's the right one */ 114 s->pci.dev = qpci_device_find(s->pci.bus, QPCI_DEVFN(4, 0)); 115 g_assert_nonnull(s->pci.dev); 116 vendor_id = qpci_config_readw(s->pci.dev, PCI_VENDOR_ID); 117 device_id = qpci_config_readw(s->pci.dev, PCI_DEVICE_ID); 118 g_assert(vendor_id == test->pci.vendor_id); 119 g_assert(device_id == test->pci.device_id); 120 s->mem_bar = qpci_iomap(s->pci.dev, 0, &barsize); 121 qpci_device_enable(s->pci.dev); 122 } else { 123 /* SysBus */ 124 global_qtest = qtest_startf("-machine %s", test->machine); 125 s->addr = test->sdhci.addr; 126 } 127 128 return s; 129 } 130 131 static void machine_stop(QSDHCI *s) 132 { 133 g_free(s->pci.dev); 134 qtest_quit(global_qtest); 135 } 136 137 static void test_machine(const void *data) 138 { 139 const struct sdhci_t *test = data; 140 QSDHCI *s; 141 142 s = machine_start(test); 143 144 check_capab_capareg(s, test->sdhci.capab.reg); 145 check_capab_readonly(s); 146 147 machine_stop(s); 148 } 149 150 int main(int argc, char *argv[]) 151 { 152 const char *arch = qtest_get_arch(); 153 char *name; 154 int i; 155 156 g_test_init(&argc, &argv, NULL); 157 for (i = 0; i < ARRAY_SIZE(models); i++) { 158 if (strcmp(arch, models[i].arch)) { 159 continue; 160 } 161 name = g_strdup_printf("sdhci/%s", models[i].machine); 162 qtest_add_data_func(name, &models[i], test_machine); 163 g_free(name); 164 } 165 166 return g_test_run(); 167 } 168