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