11e8a1faeSThomas Huth /*
21e8a1faeSThomas Huth * QTest testcase for SDHCI controllers
31e8a1faeSThomas Huth *
41e8a1faeSThomas Huth * Written by Philippe Mathieu-Daudé <f4bug@amsat.org>
51e8a1faeSThomas Huth *
61e8a1faeSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or later.
71e8a1faeSThomas Huth * See the COPYING file in the top-level directory.
81e8a1faeSThomas Huth * SPDX-License-Identifier: GPL-2.0-or-later
91e8a1faeSThomas Huth */
101e8a1faeSThomas Huth
111e8a1faeSThomas Huth #include "qemu/osdep.h"
121e8a1faeSThomas Huth #include "hw/registerfields.h"
13*907b5105SMarc-André Lureau #include "libqtest.h"
141e8a1faeSThomas Huth #include "qemu/module.h"
151e8a1faeSThomas Huth #include "libqos/pci-pc.h"
161e8a1faeSThomas Huth #include "hw/pci/pci.h"
171e8a1faeSThomas Huth #include "libqos/qgraph.h"
181e8a1faeSThomas Huth #include "libqos/sdhci.h"
191e8a1faeSThomas Huth
201e8a1faeSThomas Huth #define SDHC_CAPAB 0x40
211e8a1faeSThomas Huth FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8); /* since v2 */
221e8a1faeSThomas Huth FIELD(SDHC_CAPAB, SDMA, 22, 1);
231e8a1faeSThomas Huth FIELD(SDHC_CAPAB, SDR, 32, 3); /* since v3 */
241e8a1faeSThomas Huth FIELD(SDHC_CAPAB, DRIVER, 36, 3); /* since v3 */
251e8a1faeSThomas Huth #define SDHC_HCVER 0xFE
261e8a1faeSThomas Huth
check_specs_version(QSDHCI * s,uint8_t version)271e8a1faeSThomas Huth static void check_specs_version(QSDHCI *s, uint8_t version)
281e8a1faeSThomas Huth {
291e8a1faeSThomas Huth uint32_t v;
301e8a1faeSThomas Huth
311e8a1faeSThomas Huth v = s->readw(s, SDHC_HCVER);
321e8a1faeSThomas Huth v &= 0xff;
331e8a1faeSThomas Huth v += 1;
341e8a1faeSThomas Huth g_assert_cmpuint(v, ==, version);
351e8a1faeSThomas Huth }
361e8a1faeSThomas Huth
check_capab_capareg(QSDHCI * s,uint64_t expec_capab)371e8a1faeSThomas Huth static void check_capab_capareg(QSDHCI *s, uint64_t expec_capab)
381e8a1faeSThomas Huth {
391e8a1faeSThomas Huth uint64_t capab;
401e8a1faeSThomas Huth
411e8a1faeSThomas Huth capab = s->readq(s, SDHC_CAPAB);
421e8a1faeSThomas Huth g_assert_cmphex(capab, ==, expec_capab);
431e8a1faeSThomas Huth }
441e8a1faeSThomas Huth
check_capab_readonly(QSDHCI * s)451e8a1faeSThomas Huth static void check_capab_readonly(QSDHCI *s)
461e8a1faeSThomas Huth {
471e8a1faeSThomas Huth const uint64_t vrand = 0x123456789abcdef;
481e8a1faeSThomas Huth uint64_t capab0, capab1;
491e8a1faeSThomas Huth
501e8a1faeSThomas Huth capab0 = s->readq(s, SDHC_CAPAB);
511e8a1faeSThomas Huth g_assert_cmpuint(capab0, !=, vrand);
521e8a1faeSThomas Huth
531e8a1faeSThomas Huth s->writeq(s, SDHC_CAPAB, vrand);
541e8a1faeSThomas Huth capab1 = s->readq(s, SDHC_CAPAB);
551e8a1faeSThomas Huth g_assert_cmpuint(capab1, !=, vrand);
561e8a1faeSThomas Huth g_assert_cmpuint(capab1, ==, capab0);
571e8a1faeSThomas Huth }
581e8a1faeSThomas Huth
check_capab_baseclock(QSDHCI * s,uint8_t expec_freq)591e8a1faeSThomas Huth static void check_capab_baseclock(QSDHCI *s, uint8_t expec_freq)
601e8a1faeSThomas Huth {
611e8a1faeSThomas Huth uint64_t capab, capab_freq;
621e8a1faeSThomas Huth
631e8a1faeSThomas Huth if (!expec_freq) {
641e8a1faeSThomas Huth return;
651e8a1faeSThomas Huth }
661e8a1faeSThomas Huth capab = s->readq(s, SDHC_CAPAB);
671e8a1faeSThomas Huth capab_freq = FIELD_EX64(capab, SDHC_CAPAB, BASECLKFREQ);
681e8a1faeSThomas Huth g_assert_cmpuint(capab_freq, ==, expec_freq);
691e8a1faeSThomas Huth }
701e8a1faeSThomas Huth
check_capab_sdma(QSDHCI * s,bool supported)711e8a1faeSThomas Huth static void check_capab_sdma(QSDHCI *s, bool supported)
721e8a1faeSThomas Huth {
731e8a1faeSThomas Huth uint64_t capab, capab_sdma;
741e8a1faeSThomas Huth
751e8a1faeSThomas Huth capab = s->readq(s, SDHC_CAPAB);
761e8a1faeSThomas Huth capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA);
771e8a1faeSThomas Huth g_assert_cmpuint(capab_sdma, ==, supported);
781e8a1faeSThomas Huth }
791e8a1faeSThomas Huth
check_capab_v3(QSDHCI * s,uint8_t version)801e8a1faeSThomas Huth static void check_capab_v3(QSDHCI *s, uint8_t version)
811e8a1faeSThomas Huth {
821e8a1faeSThomas Huth uint64_t capab, capab_v3;
831e8a1faeSThomas Huth
841e8a1faeSThomas Huth if (version < 3) {
851e8a1faeSThomas Huth /* before v3 those fields are RESERVED */
861e8a1faeSThomas Huth capab = s->readq(s, SDHC_CAPAB);
871e8a1faeSThomas Huth capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, SDR);
881e8a1faeSThomas Huth g_assert_cmpuint(capab_v3, ==, 0);
891e8a1faeSThomas Huth capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, DRIVER);
901e8a1faeSThomas Huth g_assert_cmpuint(capab_v3, ==, 0);
911e8a1faeSThomas Huth }
921e8a1faeSThomas Huth }
931e8a1faeSThomas Huth
test_registers(void * obj,void * data,QGuestAllocator * alloc)941e8a1faeSThomas Huth static void test_registers(void *obj, void *data, QGuestAllocator *alloc)
951e8a1faeSThomas Huth {
961e8a1faeSThomas Huth QSDHCI *s = obj;
971e8a1faeSThomas Huth
981e8a1faeSThomas Huth check_specs_version(s, s->props.version);
991e8a1faeSThomas Huth check_capab_capareg(s, s->props.capab.reg);
1001e8a1faeSThomas Huth check_capab_readonly(s);
1011e8a1faeSThomas Huth check_capab_v3(s, s->props.version);
1021e8a1faeSThomas Huth check_capab_sdma(s, s->props.capab.sdma);
1031e8a1faeSThomas Huth check_capab_baseclock(s, s->props.baseclock);
1041e8a1faeSThomas Huth }
1051e8a1faeSThomas Huth
register_sdhci_test(void)1061e8a1faeSThomas Huth static void register_sdhci_test(void)
1071e8a1faeSThomas Huth {
1081e8a1faeSThomas Huth qos_add_test("registers", "sdhci", test_registers, NULL);
1091e8a1faeSThomas Huth }
1101e8a1faeSThomas Huth
1111e8a1faeSThomas Huth libqos_init(register_sdhci_test);
112