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
read_fifo(QTestState * qts,uint64_t reg,char * msg,size_t count)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
write_fifo(QTestState * qts,uint64_t reg,const char * msg,size_t count)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
fill_block(QTestState * qts,uint64_t reg,int count)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
sdhci_cmd_regs(QTestState * qts,uint64_t base_addr,uint16_t blksize,uint16_t blkcnt,uint32_t argument,uint16_t trnmod,uint16_t cmdreg)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
sdhci_read_cmd(QTestState * qts,uint64_t base_addr,char * msg,size_t count)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
sdhci_write_cmd(QTestState * qts,uint64_t base_addr,const char * msg,size_t count,size_t blksize)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