1*da2f02b3SShengtan Mao /* 2*da2f02b3SShengtan Mao * MMC Host Controller Commands 3*da2f02b3SShengtan Mao * 4*da2f02b3SShengtan Mao * Copyright (c) 2021 Google LLC 5*da2f02b3SShengtan Mao * 6*da2f02b3SShengtan Mao * This program is free software; you can redistribute it and/or modify it 7*da2f02b3SShengtan Mao * under the terms of the GNU General Public License as published by the 8*da2f02b3SShengtan Mao * Free Software Foundation; either version 2 of the License, or 9*da2f02b3SShengtan Mao * (at your option) any later version. 10*da2f02b3SShengtan Mao * 11*da2f02b3SShengtan Mao * This program is distributed in the hope that it will be useful, but WITHOUT 12*da2f02b3SShengtan Mao * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13*da2f02b3SShengtan Mao * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14*da2f02b3SShengtan Mao * for more details. 15*da2f02b3SShengtan Mao */ 16*da2f02b3SShengtan Mao 17*da2f02b3SShengtan Mao #include "qemu/osdep.h" 18*da2f02b3SShengtan Mao #include "sdhci-cmd.h" 19*da2f02b3SShengtan Mao #include "libqtest.h" 20*da2f02b3SShengtan Mao 21*da2f02b3SShengtan Mao static ssize_t read_fifo(QTestState *qts, uint64_t reg, char *msg, size_t count) 22*da2f02b3SShengtan Mao { 23*da2f02b3SShengtan Mao uint32_t mask = 0xff; 24*da2f02b3SShengtan Mao size_t index = 0; 25*da2f02b3SShengtan Mao uint32_t msg_frag; 26*da2f02b3SShengtan Mao int size; 27*da2f02b3SShengtan Mao while (index < count) { 28*da2f02b3SShengtan Mao size = count - index; 29*da2f02b3SShengtan Mao if (size > 4) { 30*da2f02b3SShengtan Mao size = 4; 31*da2f02b3SShengtan Mao } 32*da2f02b3SShengtan Mao msg_frag = qtest_readl(qts, reg); 33*da2f02b3SShengtan Mao while (size > 0) { 34*da2f02b3SShengtan Mao msg[index] = msg_frag & mask; 35*da2f02b3SShengtan Mao if (msg[index++] == 0) { 36*da2f02b3SShengtan Mao return index; 37*da2f02b3SShengtan Mao } 38*da2f02b3SShengtan Mao msg_frag >>= 8; 39*da2f02b3SShengtan Mao --size; 40*da2f02b3SShengtan Mao } 41*da2f02b3SShengtan Mao } 42*da2f02b3SShengtan Mao return index; 43*da2f02b3SShengtan Mao } 44*da2f02b3SShengtan Mao 45*da2f02b3SShengtan Mao static void write_fifo(QTestState *qts, uint64_t reg, const char *msg, 46*da2f02b3SShengtan Mao size_t count) 47*da2f02b3SShengtan Mao { 48*da2f02b3SShengtan Mao size_t index = 0; 49*da2f02b3SShengtan Mao uint32_t msg_frag; 50*da2f02b3SShengtan Mao int size; 51*da2f02b3SShengtan Mao int frag_i; 52*da2f02b3SShengtan Mao while (index < count) { 53*da2f02b3SShengtan Mao size = count - index; 54*da2f02b3SShengtan Mao if (size > 4) { 55*da2f02b3SShengtan Mao size = 4; 56*da2f02b3SShengtan Mao } 57*da2f02b3SShengtan Mao msg_frag = 0; 58*da2f02b3SShengtan Mao frag_i = 0; 59*da2f02b3SShengtan Mao while (frag_i < size) { 60*da2f02b3SShengtan Mao msg_frag |= ((uint32_t)msg[index++]) << (frag_i * 8); 61*da2f02b3SShengtan Mao ++frag_i; 62*da2f02b3SShengtan Mao } 63*da2f02b3SShengtan Mao qtest_writel(qts, reg, msg_frag); 64*da2f02b3SShengtan Mao } 65*da2f02b3SShengtan Mao } 66*da2f02b3SShengtan Mao 67*da2f02b3SShengtan Mao static void fill_block(QTestState *qts, uint64_t reg, int count) 68*da2f02b3SShengtan Mao { 69*da2f02b3SShengtan Mao while (--count >= 0) { 70*da2f02b3SShengtan Mao qtest_writel(qts, reg, 0); 71*da2f02b3SShengtan Mao } 72*da2f02b3SShengtan Mao } 73*da2f02b3SShengtan Mao 74*da2f02b3SShengtan Mao void sdhci_cmd_regs(QTestState *qts, uint64_t base_addr, uint16_t blksize, 75*da2f02b3SShengtan Mao uint16_t blkcnt, uint32_t argument, uint16_t trnmod, 76*da2f02b3SShengtan Mao uint16_t cmdreg) 77*da2f02b3SShengtan Mao { 78*da2f02b3SShengtan Mao qtest_writew(qts, base_addr + SDHC_BLKSIZE, blksize); 79*da2f02b3SShengtan Mao qtest_writew(qts, base_addr + SDHC_BLKCNT, blkcnt); 80*da2f02b3SShengtan Mao qtest_writel(qts, base_addr + SDHC_ARGUMENT, argument); 81*da2f02b3SShengtan Mao qtest_writew(qts, base_addr + SDHC_TRNMOD, trnmod); 82*da2f02b3SShengtan Mao qtest_writew(qts, base_addr + SDHC_CMDREG, cmdreg); 83*da2f02b3SShengtan Mao } 84*da2f02b3SShengtan Mao 85*da2f02b3SShengtan Mao ssize_t sdhci_read_cmd(QTestState *qts, uint64_t base_addr, char *msg, 86*da2f02b3SShengtan Mao size_t count) 87*da2f02b3SShengtan Mao { 88*da2f02b3SShengtan Mao sdhci_cmd_regs(qts, base_addr, count, 1, 0, 89*da2f02b3SShengtan Mao SDHC_TRNS_MULTI | SDHC_TRNS_READ | SDHC_TRNS_BLK_CNT_EN, 90*da2f02b3SShengtan Mao SDHC_READ_MULTIPLE_BLOCK | SDHC_CMD_DATA_PRESENT); 91*da2f02b3SShengtan Mao 92*da2f02b3SShengtan Mao /* read sd fifo_buffer */ 93*da2f02b3SShengtan Mao ssize_t bytes_read = read_fifo(qts, base_addr + SDHC_BDATA, msg, count); 94*da2f02b3SShengtan Mao 95*da2f02b3SShengtan Mao sdhci_cmd_regs(qts, base_addr, 0, 0, 0, 96*da2f02b3SShengtan Mao SDHC_TRNS_MULTI | SDHC_TRNS_READ | SDHC_TRNS_BLK_CNT_EN, 97*da2f02b3SShengtan Mao SDHC_STOP_TRANSMISSION); 98*da2f02b3SShengtan Mao 99*da2f02b3SShengtan Mao return bytes_read; 100*da2f02b3SShengtan Mao } 101*da2f02b3SShengtan Mao 102*da2f02b3SShengtan Mao void sdhci_write_cmd(QTestState *qts, uint64_t base_addr, const char *msg, 103*da2f02b3SShengtan Mao size_t count, size_t blksize) 104*da2f02b3SShengtan Mao { 105*da2f02b3SShengtan Mao sdhci_cmd_regs(qts, base_addr, blksize, 1, 0, 106*da2f02b3SShengtan Mao SDHC_TRNS_MULTI | SDHC_TRNS_WRITE | SDHC_TRNS_BLK_CNT_EN, 107*da2f02b3SShengtan Mao SDHC_WRITE_MULTIPLE_BLOCK | SDHC_CMD_DATA_PRESENT); 108*da2f02b3SShengtan Mao 109*da2f02b3SShengtan Mao /* write to sd fifo_buffer */ 110*da2f02b3SShengtan Mao write_fifo(qts, base_addr + SDHC_BDATA, msg, count); 111*da2f02b3SShengtan Mao fill_block(qts, base_addr + SDHC_BDATA, (blksize - count) / 4); 112*da2f02b3SShengtan Mao 113*da2f02b3SShengtan Mao sdhci_cmd_regs(qts, base_addr, 0, 0, 0, 114*da2f02b3SShengtan Mao SDHC_TRNS_MULTI | SDHC_TRNS_WRITE | SDHC_TRNS_BLK_CNT_EN, 115*da2f02b3SShengtan Mao SDHC_STOP_TRANSMISSION); 116*da2f02b3SShengtan Mao } 117