xref: /qemu/tests/qtest/libqos/sdhci-cmd.c (revision dbd9e084)
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