Initial commit
[savezelda.git] / loader / sd.c
1 // Copyright 2008  Haxx Enterprises  <bushing@gmail.com>
2 // Copyright 2008-2009  Segher Boessenkool  <segher@kernel.crashing.org>
3 // This code is licensed to you under the terms of the GNU GPL, version 2;
4 // see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
5
6
7 #include "loader.h"
8
9
10 static int fd;
11 static u32 rca; // 16 bottom bits are stuff bits
12
13
14 static int sd_hc_write8(u8 reg, u8 data)
15 {
16         u32 param[6];
17         int err;
18
19         memset(param, 0, sizeof param);
20         param[0] = reg;
21         param[3] = 1;   // reg size
22         param[4] = data;
23
24         err = ios_ioctl(fd, 1, param, sizeof param, 0, 0);
25
26         return err;
27 }
28
29 static int sd_hc_read8(u8 reg, u8 *x)
30 {
31         u32 param[6];
32         u32 data;
33         int err;
34
35         memset(param, 0, sizeof param);
36         param[0] = reg;
37         param[3] = 1;   // reg size
38         param[4] = 0;
39
40         err = ios_ioctl(fd, 2, param, sizeof param, &data, sizeof data);
41         if (err)
42                 return err;
43
44         *x = data;
45
46         return err;
47 }
48
49 static int sd_reset_card(void)
50 {
51         u32 reply;
52         int err;
53
54         err = ios_ioctl(fd, 4, 0, 0, &reply, sizeof reply);
55         if (err)
56                 return err;
57
58         rca = reply & 0xffff0000;
59
60 //      printf("sd_reset_card(): got reply = %08x\n", reply);
61
62         return 0;
63 }
64
65 static int sd_set_clock(void)
66 {
67         u32 clock;
68         int err;
69
70         clock = 1;      // half of the sdclk divisor: a power of two or zero,
71                         // should look at capabilities reg to compute this
72
73         err = ios_ioctl(fd, 6, &clock, sizeof clock, 0, 0);
74
75         return err;
76 }
77
78 static int sd_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg,
79                       u32 block_count, u32 block_size, void *addr,
80                       u32 *outreply, u32 reply_size)
81 {
82         u32 param[9];
83         u32 reply[4];
84         int err;
85
86         param[0] = cmd;
87         param[1] = cmd_type;
88         param[2] = resp_type;
89         param[3] = arg;
90         param[4] = block_count;
91         param[5] = block_size;
92         param[6] = (u32)addr;
93         param[7] = 0; // ???
94         param[8] = 0; // ???
95
96         err = ios_ioctl(fd, 7, param, sizeof param, reply, sizeof reply);
97
98         if (reply_size) // ???
99                 memcpy(outreply, reply, reply_size);
100
101         return err;
102 }
103
104
105 #define TYPE_BC 1
106 #define TYPE_BCR 2
107 #define TYPE_AC 3
108 #define TYPE_ADTC 4
109
110 #define RESPONSE_NONE 0
111 #define RESPONSE_R1 1
112 #define RESPONSE_R1B 2
113 #define RESPONSE_R2 3
114 #define RESPONSE_R3 4
115 #define RESPONSE_R4 5
116 #define RESPONSE_R5 6
117 #define RESPONSE_R6 7
118
119
120 static int sd_app_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg,
121                           u32 block_count, u32 block_size, void *addr,
122                           u32 *outreply, u32 reply_size)
123 {
124         int err;
125
126         err = sd_command(55, TYPE_AC, RESPONSE_R1, rca, 0, 0, 0, 0, 0);
127         if (err)
128                 return err;
129
130         err = sd_command(cmd, cmd_type, resp_type, arg,
131                          block_count, block_size, addr,
132                          outreply, reply_size);
133
134         return err;
135 }
136
137 static int sd_data_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg,
138                            u32 block_count, u32 block_size, void *data,
139                            u32 unk1, u32 unk2, u32 *outreply, u32 reply_size)
140 {
141         u32 param[9];
142         u32 reply[4];
143         struct ioctlv vec[3];
144         int err;
145
146         param[0] = cmd;
147         param[1] = cmd_type;
148         param[2] = resp_type;
149         param[3] = arg;
150         param[4] = block_count;
151         param[5] = block_size;
152         param[6] = (u32)data;
153         param[7] = unk1; // ???
154         param[8] = unk2; // ???
155
156         vec[0].data = param;
157         vec[0].len = sizeof param;
158         vec[1].data = data;
159         vec[1].len = block_count * block_size;
160         vec[2].data = reply;
161         vec[2].len = sizeof reply;
162
163         err = ios_ioctlv(fd, 7, 2, 1, vec);
164
165         if (reply_size) // ???
166                 memcpy(outreply, reply, reply_size);
167
168         return err;
169 }
170
171 static int sd_select(void)
172 {
173         int err;
174
175         //printf("Selecting card:\n");
176         err = sd_command(7, TYPE_AC, RESPONSE_R1B, rca, 0, 0, 0, 0, 0);
177
178         return err;
179 }
180
181 static int sd_set_blocklength(u32 len)
182 {
183         int err;
184
185         //printf("sd_set_blocklength(%u)\n", len);
186         err = sd_command(16, TYPE_AC, RESPONSE_R1, len, 0, 0, 0, 0, 0);
187
188         return err;
189 }
190
191 static int sd_set_bus_width(int width)
192 {
193         u32 arg;
194         u8 reg;
195         int err;
196
197         // First notify the card.
198         arg = (width == 4) ? 2 : 0;
199         //printf("sd_set_bus_width()\n");
200         err = sd_app_command(6, TYPE_AC, RESPONSE_R1, arg, 0, 0, 0, 0, 0);
201         if (err)
202                 return err;
203
204         // Now change the Host Control Register.
205         err = sd_hc_read8(0x28, &reg);
206         if (err)
207                 return err;
208
209         reg = (reg & ~2) | arg;
210
211         err = sd_hc_write8(0x28, reg);
212
213         return err;
214 }
215
216 int sd_read_sector(u8 *data, u32 offset)
217 {
218         u32 reply[4];
219         int err;
220
221         if (offset >= 0x800000)
222                 return -1;
223
224         err = sd_data_command(18, TYPE_AC, RESPONSE_R1, 0x200 * offset,
225                               1, 0x200, data, 1, 0, reply, sizeof reply);
226
227         sync_before_read(data, 0x200);
228
229         //printf("READ block %d\r",offset);
230         if (err)
231                 printf("SD READ %d: err=%08x, reply=%08x %08x %08x %08x\n", 
232                        offset, err, reply[0], reply[1], reply[2], reply[3]);
233
234         return err;
235 }
236
237 int sd_close(void)
238 {
239         return ios_close(fd);
240 }
241
242 int sd_init(void)
243 {
244         int err;
245
246         fd = ios_open("/dev/sdio/slot0", 0);
247         if (fd < 0)
248                 return fd;
249
250         err = sd_reset_card();
251         if (err) {
252                 printf("SD Card not present? (%d)\n", err);
253                 goto out;
254         }
255
256         // now in standby state
257
258         err = sd_select();
259         if (err)
260                 goto out;
261
262         // now in transfer state
263
264         // Some broken cards require this:
265         err = sd_set_blocklength(0x200);
266         if (err)
267                 goto out;
268
269         err = sd_set_bus_width(4);      // XXX: Should check in SCR first.
270         if (err)
271                 goto out;
272
273         err = sd_set_clock();   // XXX: Should check.
274         if (err)
275                 goto out;
276
277         return 0;
278
279  out:
280         sd_close();
281
282         return err;
283 }