1 /* 2 * MMC Host Controller Commands 3 * 4 * Copyright (c) 2021 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 "sdhci-cmd.h" 19 #include "libqtest.h" 20 21 static ssize_t read_fifo(QTestState *qts, uint64_t reg, char *msg, size_t count) 22 { 23 uint32_t mask = 0xff; 24 size_t index = 0; 25 uint32_t msg_frag; 26 int size; 27 while (index < count) { 28 size = count - index; 29 if (size > 4) { 30 size = 4; 31 } 32 msg_frag = qtest_readl(qts, reg); 33 while (size > 0) { 34 msg[index] = msg_frag & mask; 35 if (msg[index++] == 0) { 36 return index; 37 } 38 msg_frag >>= 8; 39 --size; 40 } 41 } 42 return index; 43 } 44 45 static void write_fifo(QTestState *qts, uint64_t reg, const char *msg, 46 size_t count) 47 { 48 size_t index = 0; 49 uint32_t msg_frag; 50 int size; 51 int frag_i; 52 while (index < count) { 53 size = count - index; 54 if (size > 4) { 55 size = 4; 56 } 57 msg_frag = 0; 58 frag_i = 0; 59 while (frag_i < size) { 60 msg_frag |= ((uint32_t)msg[index++]) << (frag_i * 8); 61 ++frag_i; 62 } 63 qtest_writel(qts, reg, msg_frag); 64 } 65 } 66 67 static void fill_block(QTestState *qts, uint64_t reg, int count) 68 { 69 while (--count >= 0) { 70 qtest_writel(qts, reg, 0); 71 } 72 } 73 74 void sdhci_cmd_regs(QTestState *qts, uint64_t base_addr, uint16_t blksize, 75 uint16_t blkcnt, uint32_t argument, uint16_t trnmod, 76 uint16_t cmdreg) 77 { 78 qtest_writew(qts, base_addr + SDHC_BLKSIZE, blksize); 79 qtest_writew(qts, base_addr + SDHC_BLKCNT, blkcnt); 80 qtest_writel(qts, base_addr + SDHC_ARGUMENT, argument); 81 qtest_writew(qts, base_addr + SDHC_TRNMOD, trnmod); 82 qtest_writew(qts, base_addr + SDHC_CMDREG, cmdreg); 83 } 84 85 ssize_t sdhci_read_cmd(QTestState *qts, uint64_t base_addr, char *msg, 86 size_t count) 87 { 88 sdhci_cmd_regs(qts, base_addr, count, 1, 0, 89 SDHC_TRNS_MULTI | SDHC_TRNS_READ | SDHC_TRNS_BLK_CNT_EN, 90 SDHC_READ_MULTIPLE_BLOCK | SDHC_CMD_DATA_PRESENT); 91 92 /* read sd fifo_buffer */ 93 ssize_t bytes_read = read_fifo(qts, base_addr + SDHC_BDATA, msg, count); 94 95 sdhci_cmd_regs(qts, base_addr, 0, 0, 0, 96 SDHC_TRNS_MULTI | SDHC_TRNS_READ | SDHC_TRNS_BLK_CNT_EN, 97 SDHC_STOP_TRANSMISSION); 98 99 return bytes_read; 100 } 101 102 void sdhci_write_cmd(QTestState *qts, uint64_t base_addr, const char *msg, 103 size_t count, size_t blksize) 104 { 105 sdhci_cmd_regs(qts, base_addr, blksize, 1, 0, 106 SDHC_TRNS_MULTI | SDHC_TRNS_WRITE | SDHC_TRNS_BLK_CNT_EN, 107 SDHC_WRITE_MULTIPLE_BLOCK | SDHC_CMD_DATA_PRESENT); 108 109 /* write to sd fifo_buffer */ 110 write_fifo(qts, base_addr + SDHC_BDATA, msg, count); 111 fill_block(qts, base_addr + SDHC_BDATA, (blksize - count) / 4); 112 113 sdhci_cmd_regs(qts, base_addr, 0, 0, 0, 114 SDHC_TRNS_MULTI | SDHC_TRNS_WRITE | SDHC_TRNS_BLK_CNT_EN, 115 SDHC_STOP_TRANSMISSION); 116 } 117