 3baae28150
			
		
	
	
		3baae28150
		
	
	
	
	
		
			
			Currently setup_sd_card() asks the card its address, but discard the response and use hardcoded 0x4567. Set the SDHC_CMD_RESPONSE bit to have the controller record the bus response, and read the response from the RSPREG0 register. Then we can select the card with its real address. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Message-Id: <20240702140842.54242-4-philmd@linaro.org>
		
			
				
	
	
		
			220 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QTests for NPCM7xx SD-3.0 / MMC-4.51 Host Controller
 | |
|  *
 | |
|  * Copyright (c) 2022 Google LLC
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it
 | |
|  * under the terms of the GNU General Public License as published by the
 | |
|  * Free Software Foundation; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 | |
|  * for more details.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "hw/sd/npcm7xx_sdhci.h"
 | |
| 
 | |
| #include "libqtest.h"
 | |
| #include "libqtest-single.h"
 | |
| #include "libqos/sdhci-cmd.h"
 | |
| 
 | |
| #define NPCM7XX_REG_SIZE 0x100
 | |
| #define NPCM7XX_MMC_BA 0xF0842000
 | |
| #define NPCM7XX_BLK_SIZE 512
 | |
| #define NPCM7XX_TEST_IMAGE_SIZE (1 << 20)
 | |
| 
 | |
| char *sd_path;
 | |
| 
 | |
| static QTestState *setup_sd_card(void)
 | |
| {
 | |
|     uint16_t rca;
 | |
| 
 | |
|     QTestState *qts = qtest_initf(
 | |
|         "-machine kudo-bmc "
 | |
|         "-device sd-card,drive=drive0 "
 | |
|         "-drive id=drive0,if=none,file=%s,format=raw,auto-read-only=off",
 | |
|         sd_path);
 | |
| 
 | |
|     qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_SWRST, SDHC_RESET_ALL);
 | |
|     qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_CLKCON,
 | |
|                  SDHC_CLOCK_SDCLK_EN | SDHC_CLOCK_INT_STABLE |
 | |
|                      SDHC_CLOCK_INT_EN);
 | |
|     sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_APP_CMD);
 | |
|     sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x41200000, 0, (41 << 8));
 | |
|     sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_ALL_SEND_CID);
 | |
|     sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR
 | |
|                                                     | SDHC_CMD_RESPONSE);
 | |
|     rca = qtest_readl(qts, NPCM7XX_MMC_BA + SDHC_RSPREG0) >> 16;
 | |
|     sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, rca << 16, 0,
 | |
|                    SDHC_SELECT_DESELECT_CARD);
 | |
| 
 | |
|     return qts;
 | |
| }
 | |
| 
 | |
| static void write_sdread(QTestState *qts, const char *msg)
 | |
| {
 | |
|     int fd, ret;
 | |
|     size_t len = strlen(msg);
 | |
|     char *rmsg = g_malloc(len);
 | |
| 
 | |
|     /* write message to sd */
 | |
|     fd = open(sd_path, O_WRONLY);
 | |
|     g_assert(fd >= 0);
 | |
|     ret = write(fd, msg, len);
 | |
|     close(fd);
 | |
|     g_assert(ret == len);
 | |
| 
 | |
|     /* read message using sdhci */
 | |
|     ret = sdhci_read_cmd(qts, NPCM7XX_MMC_BA, rmsg, len);
 | |
|     g_assert(ret == len);
 | |
|     g_assert(!memcmp(rmsg, msg, len));
 | |
| 
 | |
|     g_free(rmsg);
 | |
| }
 | |
| 
 | |
| /* Check MMC can read values from sd */
 | |
| static void test_read_sd(void)
 | |
| {
 | |
|     QTestState *qts = setup_sd_card();
 | |
| 
 | |
|     write_sdread(qts, "hello world");
 | |
|     write_sdread(qts, "goodbye");
 | |
| 
 | |
|     qtest_quit(qts);
 | |
| }
 | |
| 
 | |
| static void sdwrite_read(QTestState *qts, const char *msg)
 | |
| {
 | |
|     int fd, ret;
 | |
|     size_t len = strlen(msg);
 | |
|     char *rmsg = g_malloc(len);
 | |
| 
 | |
|     /* write message using sdhci */
 | |
|     sdhci_write_cmd(qts, NPCM7XX_MMC_BA, msg, len, NPCM7XX_BLK_SIZE);
 | |
| 
 | |
|     /* read message from sd */
 | |
|     fd = open(sd_path, O_RDONLY);
 | |
|     g_assert(fd >= 0);
 | |
|     ret = read(fd, rmsg, len);
 | |
|     close(fd);
 | |
|     g_assert(ret == len);
 | |
| 
 | |
|     g_assert(!memcmp(rmsg, msg, len));
 | |
| 
 | |
|     g_free(rmsg);
 | |
| }
 | |
| 
 | |
| /* Check MMC can write values to sd */
 | |
| static void test_write_sd(void)
 | |
| {
 | |
|     QTestState *qts = setup_sd_card();
 | |
| 
 | |
|     sdwrite_read(qts, "hello world");
 | |
|     sdwrite_read(qts, "goodbye");
 | |
| 
 | |
|     qtest_quit(qts);
 | |
| }
 | |
| 
 | |
| /* Check SDHCI has correct default values. */
 | |
| static void test_reset(void)
 | |
| {
 | |
|     QTestState *qts = qtest_init("-machine kudo-bmc");
 | |
|     uint64_t addr = NPCM7XX_MMC_BA;
 | |
|     uint64_t end_addr = addr + NPCM7XX_REG_SIZE;
 | |
|     uint16_t prstvals_resets[] = {NPCM7XX_PRSTVALS_0_RESET,
 | |
|                                   NPCM7XX_PRSTVALS_1_RESET,
 | |
|                                   0,
 | |
|                                   NPCM7XX_PRSTVALS_3_RESET,
 | |
|                                   0,
 | |
|                                   0};
 | |
|     int i;
 | |
|     uint32_t mask;
 | |
| 
 | |
|     while (addr < end_addr) {
 | |
|         switch (addr - NPCM7XX_MMC_BA) {
 | |
|         case SDHC_PRNSTS:
 | |
|             /*
 | |
|              * ignores bits 20 to 24: they are changed when reading registers
 | |
|              */
 | |
|             mask = 0x1f00000;
 | |
|             g_assert_cmphex(qtest_readl(qts, addr) | mask, ==,
 | |
|                             NPCM7XX_PRSNTS_RESET | mask);
 | |
|             addr += 4;
 | |
|             break;
 | |
|         case SDHC_BLKGAP:
 | |
|             g_assert_cmphex(qtest_readb(qts, addr), ==, NPCM7XX_BLKGAP_RESET);
 | |
|             addr += 1;
 | |
|             break;
 | |
|         case SDHC_CAPAB:
 | |
|             g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_CAPAB_RESET);
 | |
|             addr += 8;
 | |
|             break;
 | |
|         case SDHC_MAXCURR:
 | |
|             g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_MAXCURR_RESET);
 | |
|             addr += 8;
 | |
|             break;
 | |
|         case SDHC_HCVER:
 | |
|             g_assert_cmphex(qtest_readw(qts, addr), ==, NPCM7XX_HCVER_RESET);
 | |
|             addr += 2;
 | |
|             break;
 | |
|         case NPCM7XX_PRSTVALS:
 | |
|             for (i = 0; i < NPCM7XX_PRSTVALS_SIZE; ++i) {
 | |
|                 g_assert_cmphex(qtest_readw(qts, addr + 2 * i), ==,
 | |
|                                 prstvals_resets[i]);
 | |
|             }
 | |
|             addr += NPCM7XX_PRSTVALS_SIZE * 2;
 | |
|             break;
 | |
|         default:
 | |
|             g_assert_cmphex(qtest_readb(qts, addr), ==, 0);
 | |
|             addr += 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     qtest_quit(qts);
 | |
| }
 | |
| 
 | |
| static void drive_destroy(void)
 | |
| {
 | |
|     unlink(sd_path);
 | |
|     g_free(sd_path);
 | |
| }
 | |
| 
 | |
| static void drive_create(void)
 | |
| {
 | |
|     int fd, ret;
 | |
|     GError *error = NULL;
 | |
| 
 | |
|     /* Create a temporary raw image */
 | |
|     fd = g_file_open_tmp("sdhci_XXXXXX", &sd_path, &error);
 | |
|     if (fd == -1) {
 | |
|         fprintf(stderr, "unable to create sdhci file: %s\n", error->message);
 | |
|         g_error_free(error);
 | |
|     }
 | |
|     g_assert(sd_path != NULL);
 | |
| 
 | |
|     ret = ftruncate(fd, NPCM7XX_TEST_IMAGE_SIZE);
 | |
|     g_assert_cmpint(ret, ==, 0);
 | |
|     g_message("%s", sd_path);
 | |
|     close(fd);
 | |
| }
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
|     int ret;
 | |
| 
 | |
|     drive_create();
 | |
| 
 | |
|     g_test_init(&argc, &argv, NULL);
 | |
| 
 | |
|     qtest_add_func("npcm7xx_sdhci/reset", test_reset);
 | |
|     qtest_add_func("npcm7xx_sdhci/write_sd", test_write_sd);
 | |
|     qtest_add_func("npcm7xx_sdhci/read_sd", test_read_sd);
 | |
| 
 | |
|     ret = g_test_run();
 | |
|     drive_destroy();
 | |
|     return ret;
 | |
| }
 |