1 /*
2 * mmc / sd memory card
3 *
4 * Copyright © 2012 Richard Miller <r.miller@acm.org>
5 *
6 * Assumes only one card on the bus
7 */
8
9 #include "u.h"
10 #include "../port/lib.h"
11 #include "../port/error.h"
12 #include "mem.h"
13 #include "dat.h"
14 #include "fns.h"
15 #include "io.h"
16
17 #include "../port/sd.h"
18
19 #define CSD(end, start) rbits(csd, start, (end)-(start)+1)
20
21 typedef struct Ctlr Ctlr;
22
23 enum {
24 Inittimeout = 15,
25 Multiblock = 1,
26
27 /* Commands */
28 GO_IDLE_STATE = 0,
29 ALL_SEND_CID = 2,
30 SEND_RELATIVE_ADDR= 3,
31 SWITCH_FUNC = 6,
32 SELECT_CARD = 7,
33 SD_SEND_IF_COND = 8,
34 SEND_CSD = 9,
35 STOP_TRANSMISSION= 12,
36 SEND_STATUS = 13,
37 SET_BLOCKLEN = 16,
38 READ_SINGLE_BLOCK= 17,
39 READ_MULTIPLE_BLOCK= 18,
40 WRITE_BLOCK = 24,
41 WRITE_MULTIPLE_BLOCK= 25,
42 APP_CMD = 55, /* prefix for following app-specific commands */
43 SET_BUS_WIDTH = 6,
44 SD_SEND_OP_COND = 41,
45
46 /* Command arguments */
47 /* SD_SEND_IF_COND */
48 Voltage = 1<<8,
49 Checkpattern = 0x42,
50
51 /* SELECT_CARD */
52 Rcashift = 16,
53
54 /* SD_SEND_OP_COND */
55 Hcs = 1<<30, /* host supports SDHC & SDXC */
56 Ccs = 1<<30, /* card is SDHC or SDXC */
57 V3_3 = 3<<20, /* 3.2-3.4 volts */
58
59 /* SET_BUS_WIDTH */
60 Width1 = 0<<0,
61 Width4 = 2<<0,
62
63 /* SWITCH_FUNC */
64 Dfltspeed = 0<<0,
65 Hispeed = 1<<0,
66 Checkfunc = 0x00FFFFF0,
67 Setfunc = 0x80FFFFF0,
68 Funcbytes = 64,
69
70 /* OCR (operating conditions register) */
71 Powerup = 1<<31,
72 };
73
74 struct Ctlr {
75 SDev *dev;
76 SDio *io;
77 /* SD card registers */
78 u16int rca;
79 u32int ocr;
80 u32int cid[4];
81 u32int csd[4];
82 };
83
84 SDio *sdcardlink;
85
86 extern SDifc sdmmcifc;
87
88 static uint
rbits(u32int * p,uint start,uint len)89 rbits(u32int *p, uint start, uint len)
90 {
91 uint w, off, v;
92
93 w = start / 32;
94 off = start % 32;
95 if(off == 0)
96 v = p[w];
97 else
98 v = p[w] >> off | p[w+1] << (32-off);
99 if(len < 32)
100 return v & ((1<<len) - 1);
101 else
102 return v;
103 }
104
105 static void
identify(SDunit * unit,u32int * csd)106 identify(SDunit *unit, u32int *csd)
107 {
108 uint csize, mult;
109
110 unit->secsize = 1 << CSD(83, 80);
111 switch(CSD(127, 126)){
112 case 0: /* CSD version 1 */
113 csize = CSD(73, 62);
114 mult = CSD(49, 47);
115 unit->sectors = (csize+1) * (1<<(mult+2));
116 break;
117 case 1: /* CSD version 2 */
118 csize = CSD(69, 48);
119 unit->sectors = (csize+1) * 512LL*KiB / unit->secsize;
120 break;
121 }
122 if(unit->secsize == 1024){
123 unit->sectors <<= 1;
124 unit->secsize = 512;
125 }
126 }
127
128 static SDev*
mmcpnp(void)129 mmcpnp(void)
130 {
131 SDev *sdev;
132 Ctlr *ctl;
133
134 if(sdcardlink == nil)
135 sdcardlink = &sdio;
136 if(sdcardlink->init() < 0)
137 return nil;
138 sdev = malloc(sizeof(SDev));
139 if(sdev == nil)
140 return nil;
141 ctl = malloc(sizeof(Ctlr));
142 if(ctl == nil){
143 free(sdev);
144 return nil;
145 }
146 sdev->idno = 'M';
147 sdev->ifc = &sdmmcifc;
148 sdev->nunit = 1;
149 sdev->ctlr = ctl;
150 ctl->dev = sdev;
151 ctl->io = sdcardlink;
152 return sdev;
153 }
154
155 static int
mmcverify(SDunit * unit)156 mmcverify(SDunit *unit)
157 {
158 int n;
159 Ctlr *ctl;
160
161 ctl = unit->dev->ctlr;
162 n = ctl->io->inquiry((char*)&unit->inquiry[8], sizeof(unit->inquiry)-8);
163 if(n < 0)
164 return 0;
165 unit->inquiry[0] = SDperdisk;
166 unit->inquiry[1] = SDinq1removable;
167 unit->inquiry[4] = sizeof(unit->inquiry)-4;
168 return 1;
169 }
170
171 static int
mmcenable(SDev * dev)172 mmcenable(SDev* dev)
173 {
174 Ctlr *ctl;
175
176 ctl = dev->ctlr;
177 ctl->io->enable();
178 return 1;
179 }
180
181 static void
mmcswitchfunc(SDio * io,int arg)182 mmcswitchfunc(SDio *io, int arg)
183 {
184 uchar *buf;
185 int n;
186 u32int r[4];
187
188 n = Funcbytes;
189 buf = sdmalloc(n);
190 if(waserror()){
191 print("mmcswitchfunc error\n");
192 sdfree(buf);
193 nexterror();
194 }
195 io->iosetup(0, buf, n, 1);
196 io->cmd(SWITCH_FUNC, arg, r);
197 io->io(0, buf, n);
198 sdfree(buf);
199 poperror();
200 }
201
202 static int
mmconline(SDunit * unit)203 mmconline(SDunit *unit)
204 {
205 int hcs, i;
206 u32int r[4];
207 Ctlr *ctl;
208 SDio *io;
209
210 ctl = unit->dev->ctlr;
211 io = ctl->io;
212 assert(unit->subno == 0);
213
214 if(waserror()){
215 unit->sectors = 0;
216 return 0;
217 }
218 if(unit->sectors != 0){
219 io->cmd(SEND_STATUS, ctl->rca<<Rcashift, r);
220 poperror();
221 return 1;
222 }
223 if(waserror()){
224 unit->sectors = 0;
225 poperror();
226 return 2;
227 }
228 io->cmd(GO_IDLE_STATE, 0, r);
229 hcs = 0;
230 if(!waserror()){
231 io->cmd(SD_SEND_IF_COND, Voltage|Checkpattern, r);
232 if(r[0] == (Voltage|Checkpattern)) /* SD 2.0 or above */
233 hcs = Hcs;
234 poperror();
235 }
236 for(i = 0; i < Inittimeout; i++){
237 delay(100);
238 io->cmd(APP_CMD, 0, r);
239 io->cmd(SD_SEND_OP_COND, hcs|V3_3, r);
240 if(r[0] & Powerup)
241 break;
242 }
243 if(i == Inittimeout){
244 print("sdmmc: card won't power up\n");
245 error(Eio);
246 }
247 poperror();
248 ctl->ocr = r[0];
249 io->cmd(ALL_SEND_CID, 0, r);
250 memmove(ctl->cid, r, sizeof ctl->cid);
251 io->cmd(SEND_RELATIVE_ADDR, 0, r);
252 ctl->rca = r[0]>>16;
253 io->cmd(SEND_CSD, ctl->rca<<Rcashift, r);
254 memmove(ctl->csd, r, sizeof ctl->csd);
255 identify(unit, ctl->csd);
256 io->cmd(SELECT_CARD, ctl->rca<<Rcashift, r);
257 io->cmd(SET_BLOCKLEN, unit->secsize, r);
258 io->cmd(APP_CMD, ctl->rca<<Rcashift, r);
259 io->cmd(SET_BUS_WIDTH, Width4, r);
260 if(!waserror()){
261 mmcswitchfunc(io, Hispeed|Setfunc);
262 poperror();
263 }
264 poperror();
265 return 1;
266 }
267
268 static int
mmcrctl(SDunit * unit,char * p,int l)269 mmcrctl(SDunit *unit, char *p, int l)
270 {
271 Ctlr *ctl;
272 int i, n;
273
274 ctl = unit->dev->ctlr;
275 assert(unit->subno == 0);
276 if(unit->sectors == 0){
277 mmconline(unit);
278 if(unit->sectors == 0)
279 return 0;
280 }
281 n = snprint(p, l, "rca %4.4ux ocr %8.8ux\ncid ", ctl->rca, ctl->ocr);
282 for(i = nelem(ctl->cid)-1; i >= 0; i--)
283 n += snprint(p+n, l-n, "%8.8ux", ctl->cid[i]);
284 n += snprint(p+n, l-n, " csd ");
285 for(i = nelem(ctl->csd)-1; i >= 0; i--)
286 n += snprint(p+n, l-n, "%8.8ux", ctl->csd[i]);
287 n += snprint(p+n, l-n, "\ngeometry %llud %ld %lld 255 63\n",
288 unit->sectors, unit->secsize, unit->sectors / (255*63));
289 return n;
290 }
291
292 static long
mmcbio(SDunit * unit,int lun,int write,void * data,long nb,uvlong bno)293 mmcbio(SDunit *unit, int lun, int write, void *data, long nb, uvlong bno)
294 {
295 int len, tries;
296 ulong b;
297 u32int r[4];
298 uchar *buf;
299 Ctlr *ctl;
300 SDio *io;
301
302 USED(lun);
303 ctl = unit->dev->ctlr;
304 io = ctl->io;
305 assert(unit->subno == 0);
306 if(unit->sectors == 0)
307 error("media change");
308 buf = data;
309 len = unit->secsize;
310 if(Multiblock){
311 b = bno;
312 tries = 0;
313 while(waserror())
314 if(++tries == 3)
315 nexterror();
316 io->iosetup(write, buf, len, nb);
317 if(waserror()){
318 io->cmd(STOP_TRANSMISSION, 0, r);
319 nexterror();
320 }
321 io->cmd(write? WRITE_MULTIPLE_BLOCK: READ_MULTIPLE_BLOCK,
322 ctl->ocr & Ccs? b: b * len, r);
323 io->io(write, buf, nb * len);
324 poperror();
325 io->cmd(STOP_TRANSMISSION, 0, r);
326 poperror();
327 b += nb;
328 }else{
329 for(b = bno; b < bno + nb; b++){
330 io->iosetup(write, buf, len, 1);
331 io->cmd(write? WRITE_BLOCK : READ_SINGLE_BLOCK,
332 ctl->ocr & Ccs? b: b * len, r);
333 io->io(write, buf, len);
334 buf += len;
335 }
336 }
337 return (b - bno) * len;
338 }
339
340 static int
mmcrio(SDreq *)341 mmcrio(SDreq*)
342 {
343 return -1;
344 }
345
346 SDifc sdmmcifc = {
347 .name = "mmc",
348 .pnp = mmcpnp,
349 .enable = mmcenable,
350 .verify = mmcverify,
351 .online = mmconline,
352 .rctl = mmcrctl,
353 .bio = mmcbio,
354 .rio = mmcrio,
355 };
356