qemu/tests/qtest/npcm7xx_sdhci-test.c
<<
>>
Prefs
   1/*
   2 * QTests for NPCM7xx SD-3.0 / MMC-4.51 Host Controller
   3 *
   4 * Copyright (c) 2022 Google LLC
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License as published by the
   8 * Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14 * for more details.
  15 */
  16
  17#include "qemu/osdep.h"
  18#include "hw/sd/npcm7xx_sdhci.h"
  19
  20#include "libqtest.h"
  21#include "libqtest-single.h"
  22#include "libqos/sdhci-cmd.h"
  23
  24#define NPCM7XX_REG_SIZE 0x100
  25#define NPCM7XX_MMC_BA 0xF0842000
  26#define NPCM7XX_BLK_SIZE 512
  27#define NPCM7XX_TEST_IMAGE_SIZE (1 << 20)
  28
  29char *sd_path;
  30
  31static QTestState *setup_sd_card(void)
  32{
  33    QTestState *qts = qtest_initf(
  34        "-machine kudo-bmc "
  35        "-device sd-card,drive=drive0 "
  36        "-drive id=drive0,if=none,file=%s,format=raw,auto-read-only=off",
  37        sd_path);
  38
  39    qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_SWRST, SDHC_RESET_ALL);
  40    qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_CLKCON,
  41                 SDHC_CLOCK_SDCLK_EN | SDHC_CLOCK_INT_STABLE |
  42                     SDHC_CLOCK_INT_EN);
  43    sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_APP_CMD);
  44    sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x41200000, 0, (41 << 8));
  45    sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_ALL_SEND_CID);
  46    sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR);
  47    sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x45670000, 0,
  48                   SDHC_SELECT_DESELECT_CARD);
  49
  50    return qts;
  51}
  52
  53static void write_sdread(QTestState *qts, const char *msg)
  54{
  55    int fd, ret;
  56    size_t len = strlen(msg);
  57    char *rmsg = g_malloc(len);
  58
  59    /* write message to sd */
  60    fd = open(sd_path, O_WRONLY);
  61    g_assert(fd >= 0);
  62    ret = write(fd, msg, len);
  63    close(fd);
  64    g_assert(ret == len);
  65
  66    /* read message using sdhci */
  67    ret = sdhci_read_cmd(qts, NPCM7XX_MMC_BA, rmsg, len);
  68    g_assert(ret == len);
  69    g_assert(!memcmp(rmsg, msg, len));
  70
  71    g_free(rmsg);
  72}
  73
  74/* Check MMC can read values from sd */
  75static void test_read_sd(void)
  76{
  77    QTestState *qts = setup_sd_card();
  78
  79    write_sdread(qts, "hello world");
  80    write_sdread(qts, "goodbye");
  81
  82    qtest_quit(qts);
  83}
  84
  85static void sdwrite_read(QTestState *qts, const char *msg)
  86{
  87    int fd, ret;
  88    size_t len = strlen(msg);
  89    char *rmsg = g_malloc(len);
  90
  91    /* write message using sdhci */
  92    sdhci_write_cmd(qts, NPCM7XX_MMC_BA, msg, len, NPCM7XX_BLK_SIZE);
  93
  94    /* read message from sd */
  95    fd = open(sd_path, O_RDONLY);
  96    g_assert(fd >= 0);
  97    ret = read(fd, rmsg, len);
  98    close(fd);
  99    g_assert(ret == len);
 100
 101    g_assert(!memcmp(rmsg, msg, len));
 102
 103    g_free(rmsg);
 104}
 105
 106/* Check MMC can write values to sd */
 107static void test_write_sd(void)
 108{
 109    QTestState *qts = setup_sd_card();
 110
 111    sdwrite_read(qts, "hello world");
 112    sdwrite_read(qts, "goodbye");
 113
 114    qtest_quit(qts);
 115}
 116
 117/* Check SDHCI has correct default values. */
 118static void test_reset(void)
 119{
 120    QTestState *qts = qtest_init("-machine kudo-bmc");
 121    uint64_t addr = NPCM7XX_MMC_BA;
 122    uint64_t end_addr = addr + NPCM7XX_REG_SIZE;
 123    uint16_t prstvals_resets[] = {NPCM7XX_PRSTVALS_0_RESET,
 124                                  NPCM7XX_PRSTVALS_1_RESET,
 125                                  0,
 126                                  NPCM7XX_PRSTVALS_3_RESET,
 127                                  0,
 128                                  0};
 129    int i;
 130    uint32_t mask;
 131
 132    while (addr < end_addr) {
 133        switch (addr - NPCM7XX_MMC_BA) {
 134        case SDHC_PRNSTS:
 135            /*
 136             * ignores bits 20 to 24: they are changed when reading registers
 137             */
 138            mask = 0x1f00000;
 139            g_assert_cmphex(qtest_readl(qts, addr) | mask, ==,
 140                            NPCM7XX_PRSNTS_RESET | mask);
 141            addr += 4;
 142            break;
 143        case SDHC_BLKGAP:
 144            g_assert_cmphex(qtest_readb(qts, addr), ==, NPCM7XX_BLKGAP_RESET);
 145            addr += 1;
 146            break;
 147        case SDHC_CAPAB:
 148            g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_CAPAB_RESET);
 149            addr += 8;
 150            break;
 151        case SDHC_MAXCURR:
 152            g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_MAXCURR_RESET);
 153            addr += 8;
 154            break;
 155        case SDHC_HCVER:
 156            g_assert_cmphex(qtest_readw(qts, addr), ==, NPCM7XX_HCVER_RESET);
 157            addr += 2;
 158            break;
 159        case NPCM7XX_PRSTVALS:
 160            for (i = 0; i < NPCM7XX_PRSTVALS_SIZE; ++i) {
 161                g_assert_cmphex(qtest_readw(qts, addr + 2 * i), ==,
 162                                prstvals_resets[i]);
 163            }
 164            addr += NPCM7XX_PRSTVALS_SIZE * 2;
 165            break;
 166        default:
 167            g_assert_cmphex(qtest_readb(qts, addr), ==, 0);
 168            addr += 1;
 169        }
 170    }
 171
 172    qtest_quit(qts);
 173}
 174
 175static void drive_destroy(void)
 176{
 177    unlink(sd_path);
 178    g_free(sd_path);
 179}
 180
 181static void drive_create(void)
 182{
 183    int fd, ret;
 184    GError *error = NULL;
 185
 186    /* Create a temporary raw image */
 187    fd = g_file_open_tmp("sdhci_XXXXXX", &sd_path, &error);
 188    if (fd == -1) {
 189        fprintf(stderr, "unable to create sdhci file: %s\n", error->message);
 190        g_error_free(error);
 191    }
 192    g_assert(sd_path != NULL);
 193
 194    ret = ftruncate(fd, NPCM7XX_TEST_IMAGE_SIZE);
 195    g_assert_cmpint(ret, ==, 0);
 196    g_message("%s", sd_path);
 197    close(fd);
 198}
 199
 200int main(int argc, char **argv)
 201{
 202    int ret;
 203
 204    drive_create();
 205
 206    g_test_init(&argc, &argv, NULL);
 207
 208    qtest_add_func("npcm7xx_sdhci/reset", test_reset);
 209    qtest_add_func("npcm7xx_sdhci/write_sd", test_write_sd);
 210    qtest_add_func("npcm7xx_sdhci/read_sd", test_read_sd);
 211
 212    ret = g_test_run();
 213    drive_destroy();
 214    return ret;
 215}
 216