forked from mit-pdos/xv6-riscv
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsdcard.c
157 lines (132 loc) · 3.76 KB
/
sdcard.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include "types.h"
#include "riscv.h"
#include "defs.h"
#include "pinmux.h"
#include "spi.h"
#include "sdcard.h"
#include "fs.h"
struct spi_config spi = {
.port = 1,
.baud_rate = 400000,
.polarity = 0,
.phase = 0,
};
static uint8 spi_transfer(uint8 data) {
uint8 rx_data;
int ret = pl022_spi_xfer(8, &data, &rx_data, SPI_XFER_BEGIN);
if(ret < 0){
return -1;
}
return rx_data;
}
// Helper function to send a command to the SD card over SPI
static uint8 sd_send_command(uint8 cmd, uint32 arg, uint8 crc) {
spi_transfer(0x40 | cmd); // Command token
spi_transfer((arg >> 24) & 0xFF); // Argument: 4 bytes
spi_transfer((arg >> 16) & 0xFF);
spi_transfer((arg >> 8) & 0xFF);
spi_transfer(arg & 0xFF);
spi_transfer(crc); // CRC for CMD0 and CMD8; for others, any value is fine.
// Wait for a response (timeout after a certain number of tries)
for (int i = 0; i < 8; i++) {
uint8 response = spi_transfer(0xFF);
if (response != 0xFF) {
return response;
}
}
return 0xFF; // Timeout
}
// Initialize the SD card
int sd_init(uint8 iface, uint8 cs, uint8 miso, uint8 clk, uint8 mosi) {
if (init_spi_bus(&spi, cs, miso, clk, mosi) < 0) {
return -1;
}
if (pl022_spi_probe() < 0) {
return -1;
}
if (pl022_spi_claim_bus() < 0) {
return -1;
}
if (pl022_spi_set_speed(&spi) < 0) {
return -1;
}
if (pl022_spi_set_mode(&spi) < 0) {
return -1;
}
// Send at least 74 clock cycles with CS high to initialize card
pl022_spi_cs_hold(1);
for (int i = 0; i < 10; i++) {
spi_transfer(0xFF);
}
pl022_spi_cs_auto();
// Dummy command
sd_send_command(CMD0, 0, 0x95);
// CMD0: reset the card
if (sd_send_command(CMD0, 0, 0x95) != 0x01) {
return -1; // Card should respond with "in idle" (0x01)
}
// CMD8: check voltage range
if (sd_send_command(CMD8, 0x1AA, 0x87) != 0x01) {
return -1; // Card should respond with 0x01 if compatible
} else {
// Read remaining R7 response bytes
uint8 r7[4];
for(int i = 0; i < 4; i++) {
r7[i] = spi_transfer(0xFF);
}
// Check voltage acceptance pattern
if (!((r7[2] == 0x01) && (r7[3] == 0xAA))){
return -1; // Card is not SD v2.0+
}
}
// ACMD41: initialize card
while (sd_send_command(CMD55, 0, 0xFF), sd_send_command(ACMD41, 0x40000000, 0xFF) != 0x00) {}
return 0;
}
int sd_deinit(){
while(spi_transfer(0xFF) != 0xFF){};
if (pl022_spi_release_bus() < 0) {
return -1;
}
return 0;
}
// Read a 512-bytes block from the SD Card
int sd_read_block(uint32 blockno, uint8 *buffer) {
// Send read command
if (sd_send_command(CMD17, blockno, 0xFF) != 0x00) {
return -1;
}
// Wait for data token (0xFE)
while (spi_transfer(0xFF) != 0xFE){}
// Read data block
for(int i = 0; i < SD_BSIZE; i++){
buffer[i] = spi_transfer(0xFF);
}
// Read CRC (2 bytes, discard)
spi_transfer(0xFF);
spi_transfer(0xFF);
return 0;
}
// Write a 512-bytes block from the SD Card
int sd_write_block(uint32 blockno, const uint8 *buffer) {
// Send write command
if (sd_send_command(CMD24, blockno, 0xFF) != 0x00) {
return -1;
}
// Send data token (0xFE)
spi_transfer(0xFE);
// Write data block
for(int i = 0; i < SD_BSIZE; i++){
spi_transfer(buffer[i]);
}
// Write CRC (2 bytes, dummy)
spi_transfer(0xFF);
spi_transfer(0xFF);
// Check data response token
if ((spi_transfer(0xFF) & 0x1F) != 0x05) {
return -1;
}
// Wait for the card to finish writing
while (spi_transfer(0xFF) == 0x00) {}
return 0;
}