xref: /plan9-contrib/sys/src/nboot/zynq/mmc.c (revision 529c1f209803c78c4f2cda11b13818a57f01c872)
1 #include <u.h>
2 #include "dat.h"
3 #include "fns.h"
4 #include "mem.h"
5 
6 enum {
7 	Sectsz = 0x200,
8 	Dirsz = 0x20,
9 	Maxpath = 64,
10 	Fat12 = 1,
11 	Fat16 = 2,
12 	Fat32 = 4,
13 };
14 
15 typedef struct File File;
16 typedef struct Dir Dir;
17 typedef struct Pbs Pbs;
18 typedef struct Pbs32 Pbs32;
19 typedef struct Fat Fat;
20 
21 struct Fat
22 {
23 	ulong ver;
24 	ulong clustsize;
25 	ulong eofmark;
26 	ulong partlba;
27 	ulong fatlba;
28 	ulong dirstart; /* LBA for FAT16, cluster for FAT32 */
29 	ulong dirents;
30 	ulong datalba;
31 };
32 
33 struct File
34 {
35 	Fat *fat;
36 	ulong lba;
37 	ulong clust;
38 	ulong lbaoff;
39 	ulong len;
40 	uchar *rp;
41 	uchar *ep;
42 	uchar buf[Sectsz];
43 };
44 
45 struct Dir
46 {
47 	char name[11];
48 	uchar attr;
49 	uchar reserved;
50 	uchar ctime;
51 	uchar ctime[2];
52 	uchar cdate[2];
53 	uchar adate[2];
54 	uchar starthi[2];
55 	uchar mtime[2];
56 	uchar mdate[2];
57 	uchar startlo[2];
58 	uchar len[4];
59 };
60 
61 struct Pbs
62 {
63 	uchar magic[3];
64 	uchar version[8];
65 	uchar sectsize[2];
66 	uchar clustsize;
67 	uchar nreserv[2];
68 	uchar nfats;
69 	uchar rootsize[2];
70 	uchar volsize[2];
71 	uchar mediadesc;
72 	uchar fatsize[2];
73 	uchar trksize[2];
74 	uchar nheads[2];
75 	uchar nhidden[4];
76 	uchar bigvolsize[4];
77 	uchar driveno;
78 	uchar reserved0;
79 	uchar bootsig;
80 	uchar volid[4];
81 	uchar label[11];
82 	uchar type[8];
83 };
84 
85 struct Pbs32
86 {
87 	uchar common[36];
88 	uchar fatsize[4];
89 	uchar flags[2];
90 	uchar ver[2];
91 	uchar rootclust[4];
92 	uchar fsinfo[2];
93 	uchar bootbak[2];
94 	uchar reserved0[12];
95 	uchar driveno;
96 	uchar reserved1;
97 	uchar bootsig;
98 	uchar volid[4];
99 	uchar label[11];
100 	uchar type[8];
101 };
102 
103 enum {
104 	Initfreq	= 400000,	/* initialisation frequency for MMC */
105 	SDfreq		= 25000000,	/* standard SD frequency */
106 	DTO		= 14,		/* data timeout exponent (guesswork) */
107 };
108 
109 enum {
110 	/* Controller registers */
111 	Sysaddr			= 0x00>>2,
112 	Blksizecnt		= 0x04>>2,
113 	Arg1			= 0x08>>2,
114 	Cmdtm			= 0x0c>>2,
115 	Resp0			= 0x10>>2,
116 	Resp1			= 0x14>>2,
117 	Resp2			= 0x18>>2,
118 	Resp3			= 0x1c>>2,
119 	Data			= 0x20>>2,
120 	Status			= 0x24>>2,
121 	Control0		= 0x28>>2,
122 	Control1		= 0x2c>>2,
123 	Interrupt		= 0x30>>2,
124 	Irptmask		= 0x34>>2,
125 	Irpten			= 0x38>>2,
126 	Capabilites		= 0x40>>2,
127 	Forceirpt		= 0x50>>2,
128 	Boottimeout		= 0x60>>2,
129 	Dbgsel			= 0x64>>2,
130 	Spiintspt		= 0xf0>>2,
131 	Slotisrver		= 0xfc>>2,
132 
133 	/* Control0 */
134 	Dwidth4			= 1<<1,
135 	Dwidth1			= 0<<1,
136 
137 	/* Control1 */
138 	Srstdata		= 1<<26,	/* reset data circuit */
139 	Srstcmd			= 1<<25,	/* reset command circuit */
140 	Srsthc			= 1<<24,	/* reset complete host controller */
141 	Datatoshift		= 16,		/* data timeout unit exponent */
142 	Datatomask		= 0xF0000,
143 	Clkfreq8shift		= 8,		/* SD clock base divider LSBs */
144 	Clkfreq8mask		= 0xFF00,
145 	Clkfreqms2shift		= 6,		/* SD clock base divider MSBs */
146 	Clkfreqms2mask		= 0xC0,
147 	Clkgendiv		= 0<<5,		/* SD clock divided */
148 	Clkgenprog		= 1<<5,		/* SD clock programmable */
149 	Clken			= 1<<2,		/* SD clock enable */
150 	Clkstable		= 1<<1,
151 	Clkintlen		= 1<<0,		/* enable internal EMMC clocks */
152 
153 	/* Cmdtm */
154 	Indexshift		= 24,
155 	Suspend			= 1<<22,
156 	Resume			= 2<<22,
157 	Abort			= 3<<22,
158 	Isdata			= 1<<21,
159 	Ixchken			= 1<<20,
160 	Crcchken		= 1<<19,
161 	Respmask		= 3<<16,
162 	Respnone		= 0<<16,
163 	Resp136			= 1<<16,
164 	Resp48			= 2<<16,
165 	Resp48busy		= 3<<16,
166 	Multiblock		= 1<<5,
167 	Host2card		= 0<<4,
168 	Card2host		= 1<<4,
169 	Autocmd12		= 1<<2,
170 	Autocmd23		= 2<<2,
171 	Blkcnten		= 1<<1,
172 	Dmaen			= 1<<0,
173 
174 	/* Interrupt */
175 	Acmderr		= 1<<24,
176 	Denderr		= 1<<22,
177 	Dcrcerr		= 1<<21,
178 	Dtoerr		= 1<<20,
179 	Cbaderr		= 1<<19,
180 	Cenderr		= 1<<18,
181 	Ccrcerr		= 1<<17,
182 	Ctoerr		= 1<<16,
183 	Err		= 1<<15,
184 	Cardintr	= 1<<8,
185 	Cardinsert	= 1<<6,
186 	Readrdy		= 1<<5,
187 	Writerdy	= 1<<4,
188 	Dmaintr		= 1<<3,
189 	Datadone	= 1<<1,
190 	Cmddone		= 1<<0,
191 
192 	/* Status */
193 	Present		= 1<<18,
194 	Bufread		= 1<<11,
195 	Bufwrite	= 1<<10,
196 	Readtrans	= 1<<9,
197 	Writetrans	= 1<<8,
198 	Datactive	= 1<<2,
199 	Datinhibit	= 1<<1,
200 	Cmdinhibit	= 1<<0,
201 
202 	Inittimeout	= 15,
203 //	Multiblock	= 1,
204 
205 	/* Commands */
206 	GO_IDLE_STATE	= 0,
207 	ALL_SEND_CID	= 2,
208 	SEND_RELATIVE_ADDR= 3,
209 	SELECT_CARD	= 7,
210 	SD_SEND_IF_COND	= 8,
211 	SEND_CSD	= 9,
212 	STOP_TRANSMISSION= 12,
213 	SEND_STATUS	= 13,
214 	SET_BLOCKLEN	= 16,
215 	READ_SINGLE_BLOCK= 17,
216 	READ_MULTIPLE_BLOCK= 18,
217 	WRITE_BLOCK	= 24,
218 	WRITE_MULTIPLE_BLOCK= 25,
219 	APP_CMD		= 55,	/* prefix for following app-specific commands */
220 	SET_BUS_WIDTH	= 6,
221 	SD_SEND_OP_COND	= 41,
222 
223 	/* Command arguments */
224 	/* SD_SEND_IF_COND */
225 	Voltage		= 1<<8,
226 	Checkpattern	= 0x42,
227 
228 	/* SELECT_CARD */
229 	Rcashift	= 16,
230 
231 	/* SD_SEND_OP_COND */
232 	Hcs	= 1<<30,	/* host supports SDHC & SDXC */
233 	Ccs	= 1<<30,	/* card is SDHC or SDXC */
234 	V3_3	= 3<<20,	/* 3.2-3.4 volts */
235 
236 	/* SET_BUS_WIDTH */
237 	Width1	= 0<<0,
238 	Width4	= 2<<0,
239 
240 	/* OCR (operating conditions register) */
241 	Powerup	= 1<<31,
242 };
243 
244 static int cmdinfo[64] = {
245 [0]  Ixchken,
246 [2]  Resp136,
247 [3]  Resp48 | Ixchken | Crcchken,
248 [6]  Resp48 | Ixchken | Crcchken,
249 [7]  Resp48busy | Ixchken | Crcchken,
250 [8]  Resp48 | Ixchken | Crcchken,
251 [9]  Resp136,
252 [12] Resp48busy | Ixchken | Crcchken,
253 [13] Resp48 | Ixchken | Crcchken,
254 [16] Resp48,
255 [17] Resp48 | Isdata | Card2host | Ixchken | Crcchken | Dmaen,
256 [18] Resp48 | Isdata | Card2host | Multiblock | Blkcnten | Ixchken | Crcchken | Dmaen,
257 [24] Resp48 | Isdata | Host2card | Ixchken | Crcchken | Dmaen,
258 [25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken | Dmaen,
259 [41] Resp48,
260 [55] Resp48 | Ixchken | Crcchken,
261 };
262 
263 typedef struct Ctlr Ctlr;
264 struct Ctlr {
265 	u32int	*regs;
266 	ulong	extclk;
267 
268 	/* SD card registers */
269 	u16int	rca;
270 	u32int	ocr;
271 	u32int	cid[4];
272 	u32int	csd[4];
273 };
274 static Ctlr ctlr = {
275 	.regs	= (u32int*)0xE0101000,
276 	.extclk	= 100000000,
277 };
278 
279 
280 static ushort
GETSHORT(void * v)281 GETSHORT(void *v)
282 {
283 	uchar *p = v;
284 	return p[0] | p[1]<<8;
285 }
286 static ulong
GETLONG(void * v)287 GETLONG(void *v)
288 {
289 	uchar *p = v;
290 	return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
291 }
292 
293 static int
memcmp(void * src,void * dst,int n)294 memcmp(void *src, void *dst, int n)
295 {
296 	uchar *d = dst;
297 	uchar *s = src;
298 	int r = 0;
299 
300 	while(n-- > 0){
301 		r = *d++ - *s++;
302 		if(r != 0)
303 			break;
304 	}
305 
306 	return r;
307 }
308 
309 static uint
clkdiv(uint d)310 clkdiv(uint d)
311 {
312 	uint v;
313 
314 	v = (d << Clkfreq8shift) & Clkfreq8mask;
315 	v |= ((d >> 8) << Clkfreqms2shift) & Clkfreqms2mask;
316 	return v;
317 }
318 
319 static int
mmcwait(int mask)320 mmcwait(int mask)
321 {
322 	int i, t;
323 
324 	t = 0;
325 	while(((i=ctlr.regs[Interrupt])&mask) == 0)
326 		if(t++ > 10000000)
327 			break;
328 
329 	return i;
330 }
331 
332 static int
mmccmd(u32int cmd,u32int arg,u32int * resp)333 mmccmd(u32int cmd, u32int arg, u32int *resp)
334 {
335 	u32int *r;
336 	u32int c;
337 	int i;
338 
339 	c = (cmd << Indexshift) | cmdinfo[cmd];
340 
341 	r = ctlr.regs;
342 	if(r[Status] & Cmdinhibit){
343 		print("mmc: need to reset Cmdinhibit intr %x stat %x\n",
344 			r[Interrupt], r[Status]);
345 		r[Control1] |= Srstcmd;
346 		while(r[Control1] & Srstcmd)
347 			;
348 		while(r[Status] & Cmdinhibit)
349 			;
350 	}
351 	if((c & Isdata || (c & Respmask) == Resp48busy) && r[Status] & Datinhibit){
352 		print("mmc: need to reset Datinhibit intr %x stat %x\n",
353 			r[Interrupt], r[Status]);
354 		r[Control1] |= Srstdata;
355 		while(r[Control1] & Srstdata)
356 			;
357 		while(r[Status] & Datinhibit)
358 			;
359 	}
360 	r[Arg1] = arg;
361 	if((i = r[Interrupt]) != 0){
362 		if(i != Cardinsert)
363 			print("mmc: before command, intr was %x\n", i);
364 		r[Interrupt] = i;
365 	}
366 	r[Cmdtm] = c;
367 
368 	i = mmcwait(Cmddone|Err);
369 	if((i&(Cmddone|Err)) != Cmddone){
370 		if((i&~Err) != Ctoerr)
371 			print("mmc: CMD%d error intr %x stat %x\n", cmd, i, r[Status]);
372 		r[Interrupt] = i;
373 		if(r[Status]&Cmdinhibit){
374 			r[Control1] |= Srstcmd;
375 			while(r[Control1]&Srstcmd)
376 				;
377 		}
378 		return -1;
379 	}
380 	r[Interrupt] = i & ~(Datadone|Readrdy|Writerdy);
381 	switch(c & Respmask){
382 	case Resp136:
383 		resp[0] = r[Resp0]<<8;
384 		resp[1] = r[Resp0]>>24 | r[Resp1]<<8;
385 		resp[2] = r[Resp1]>>24 | r[Resp2]<<8;
386 		resp[3] = r[Resp2]>>24 | r[Resp3]<<8;
387 		break;
388 	case Resp48:
389 	case Resp48busy:
390 		resp[0] = r[Resp0];
391 		break;
392 	case Respnone:
393 		resp[0] = 0;
394 		break;
395 	}
396 	if((c & Respmask) == Resp48busy){
397 		r[Irpten] = Datadone|Err;
398 		i = mmcwait(Cmddone|Err);
399 		if(i)
400 			r[Interrupt] = i;
401 		r[Irpten] = 0;
402 		if((i & Datadone) == 0)
403 			print("mmc: no Datadone after CMD%d\n", cmd);
404 		if(i & Err)
405 			print("mmc: CMD%d error interrupt %x\n", cmd, i);
406 	}
407 
408 	/*
409 	 * Once card is selected, use faster clock
410 	 */
411 	if(cmd == SELECT_CARD){
412 		sleep(10);
413 		r[Control1] = clkdiv(ctlr.extclk / SDfreq - 1) |
414 			DTO << Datatoshift | Clkgendiv | Clken | Clkintlen;
415 		for(i = 0; i < 1000; i++){
416 			sleep(1);
417 			if(r[Control1] & Clkstable)
418 				break;
419 		}
420 		sleep(10);
421 	}
422 
423 	/*
424 	 * If card bus width changes, change host bus width
425 	 */
426 	if(cmd == SET_BUS_WIDTH)
427 		switch(arg){
428 		case 0:
429 			r[Control0] &= ~Dwidth4;
430 			break;
431 		case 2:
432 			r[Control0] |= Dwidth4;
433 			break;
434 		}
435 	return 0;
436 }
437 
438 static int
mmconline(void)439 mmconline(void)
440 {
441 	u32int r[4];
442 	int hcs, i;
443 
444 	mmccmd(GO_IDLE_STATE, 0, r);
445 
446 	hcs = 0;
447 	if(mmccmd(SD_SEND_IF_COND, Voltage|Checkpattern, r) == 0){
448 		if(r[0] == (Voltage|Checkpattern))	/* SD 2.0 or above */
449 			hcs = Hcs;
450 	}
451 	for(i = 0; i < Inittimeout; i++){
452 		sleep(100);
453 		mmccmd(APP_CMD, 0, r);
454 		mmccmd(SD_SEND_OP_COND, hcs|V3_3, r);
455 		if(r[0] & Powerup)
456 			break;
457 	}
458 	if(i == Inittimeout){
459 		print("mmc: card won't power up\n");
460 		return -1;
461 	}
462 	ctlr.ocr = r[0];
463 	mmccmd(ALL_SEND_CID, 0, r);
464 	memcpy(ctlr.cid, r, sizeof ctlr.cid);
465 	mmccmd(SEND_RELATIVE_ADDR, 0, r);
466 	ctlr.rca = r[0]>>16;
467 	mmccmd(SEND_CSD, ctlr.rca<<Rcashift, r);
468 	memcpy(ctlr.csd, r, sizeof ctlr.csd);
469 	mmccmd(SELECT_CARD, ctlr.rca<<Rcashift, r);
470 	mmccmd(SET_BLOCKLEN, Sectsz, r);
471 	mmccmd(APP_CMD, ctlr.rca<<Rcashift, r);
472 	mmccmd(SET_BUS_WIDTH, Width4, r);
473 	return 0;
474 }
475 
476 static int
mmcinit(void)477 mmcinit(void)
478 {
479 	u32int *r;
480 	int i;
481 
482 	r = ctlr.regs;
483 	r[Control1] = Srsthc;
484 	for(i = 0; i < 100; i++){
485 		sleep(10);
486 		if((r[Control1] & Srsthc) == 0)
487 			break;
488 	}
489 	if(i == 100){
490 		print("mmc: reset timeout!\n");
491 		return -1;
492 	}
493 	r[Control1] = clkdiv(ctlr.extclk / Initfreq - 1) | DTO << Datatoshift |
494 		Clkgendiv | Clken | Clkintlen;
495 	for(i = 0; i < 1000; i++){
496 		sleep(1);
497 		if(r[Control1] & Clkstable)
498 			break;
499 	}
500 	if(i == 1000){
501 		print("mmc: SD clock won't initialise!\n");
502 		return -1;
503 	}
504 	r[Irptmask] = ~(Dtoerr|Cardintr|Dmaintr);
505 	return mmconline();
506 }
507 
508 static int
mmcread(ulong bno,uchar buf[Sectsz])509 mmcread(ulong bno, uchar buf[Sectsz])
510 {
511 	u32int *r, rr[4];
512 	int i, t;
513 
514 	r = ctlr.regs;
515 	for(t=0; t<3; t++){
516 		r[Sysaddr] = (u32int)buf;
517 		r[Blksizecnt] = 7<<12 | 1<<16 | Sectsz;
518 		r[Irpten] = Datadone|Err;
519 		mmccmd(READ_SINGLE_BLOCK, ctlr.ocr & Ccs? bno : bno*Sectsz, rr);
520 		i = mmcwait(Datadone|Err);
521 		if(i)
522 			r[Interrupt] = i;
523 		r[Irpten] = 0;
524 		if((i & Err) != 0)
525 			print("mmcread: error intr %x stat %x\n", i, r[Status]);
526 		else if((i & Datadone) == 0)
527 			print("mmcread: timeout intr %x stat %x\n", i, r[Status]);
528 		else
529 			return 0;
530 	}
531 	return -1;
532 }
533 
534 static int
dirname(Dir * d,char buf[Maxpath])535 dirname(Dir *d, char buf[Maxpath])
536 {
537 	char c, *x;
538 
539 	if(d->attr == 0x0F || *d->name <= 0)
540 		return -1;
541 	memcpy(buf, d->name, 8);
542 	x = buf+8;
543 	while(x > buf && x[-1] == ' ')
544 		x--;
545 	if(d->name[8] != ' '){
546 		*x++ = '.';
547 		memcpy(x, d->name+8, 3);
548 		x += 3;
549 	}
550 	while(x > buf && x[-1] == ' ')
551 		x--;
552 	*x = 0;
553 	x = buf;
554 	while(c = *x){
555 		if(c >= 'A' && c <= 'Z'){
556 			c -= 'A';
557 			c += 'a';
558 		}
559 		*x++ = c;
560 	}
561 	return x - buf;
562 }
563 
564 static ulong
dirclust(Dir * d)565 dirclust(Dir *d)
566 {
567 	return GETSHORT(d->starthi)<<16 | GETSHORT(d->startlo);
568 }
569 
570 static void
fileinit(File * fp,Fat * fat,ulong lba)571 fileinit(File *fp, Fat *fat, ulong lba)
572 {
573 	fp->fat = fat;
574 	fp->lba = lba;
575 	fp->len = 0;
576 	fp->lbaoff = 0;
577 	fp->clust = ~0U;
578 	fp->rp = fp->ep = fp->buf + Sectsz;
579 }
580 
581 static ulong
readnext(File * fp,ulong clust)582 readnext(File *fp, ulong clust)
583 {
584 	Fat *fat = fp->fat;
585 	uchar tmp[2], *p;
586 	ulong idx, lba;
587 
588 	if(fat->ver == Fat12)
589 		idx = (3*clust)/2;
590 	else
591 		idx = clust*fat->ver;
592 	lba = fat->fatlba + (idx / Sectsz);
593 	if(mmcread(lba, fp->buf))
594 		memset(fp->buf, 0xff, Sectsz);
595 	p = &fp->buf[idx % Sectsz];
596 	if(p == &fp->buf[Sectsz-1]){
597 		tmp[0] = *p;
598 		if(mmcread(++lba, fp->buf))
599 			memset(fp->buf, 0xff, Sectsz);
600 		tmp[1] = fp->buf[0];
601 		p = tmp;
602 	}
603 	if(fat->ver == Fat32)
604 		return GETLONG(p) & 0xfffffff;
605 	idx = GETSHORT(p);
606 	if(fat->ver == Fat12){
607 		if(clust & 1)
608 			idx >>= 4;
609 		idx &= 0xfff;
610 	}
611 	return idx;
612 }
613 
614 static int
fileread(File * fp,void * data,int len)615 fileread(File *fp, void *data, int len)
616 {
617 	Fat *fat = fp->fat;
618 
619 	if(fp->len > 0 && fp->rp >= fp->ep){
620 		if(fp->clust != ~0U){
621 			if(fp->lbaoff % fat->clustsize == 0){
622 				if(fp->clust < 2 || fp->clust >= fat->eofmark)
623 					return -1;
624 				fp->lbaoff = (fp->clust - 2) * fat->clustsize;
625 				fp->clust = readnext(fp, fp->clust);
626 				fp->lba = fp->lbaoff + fat->datalba;
627 			}
628 			fp->lbaoff++;
629 		}
630 		if(mmcread(fp->lba++, fp->rp = fp->buf))
631 			return -1;
632 	}
633 	if(fp->len < len)
634 		len = fp->len;
635 	if(len > (fp->ep - fp->rp))
636 		len = fp->ep - fp->rp;
637 	memcpy(data, fp->rp, len);
638 	fp->rp += len;
639 	fp->len -= len;
640 	return len;
641 }
642 
643 static int
fatwalk(File * fp,Fat * fat,char * path)644 fatwalk(File *fp, Fat *fat, char *path)
645 {
646 	char name[Maxpath], *end;
647 	int i, j;
648 	Dir d;
649 
650 	if(fat->ver == Fat32){
651 		fileinit(fp, fat, 0);
652 		fp->clust = fat->dirstart;
653 		fp->len = ~0U;
654 	}else{
655 		fileinit(fp, fat, fat->dirstart);
656 		fp->len = fat->dirents * Dirsz;
657 	}
658 	for(;;){
659 		if(fileread(fp, &d, Dirsz) != Dirsz)
660 			break;
661 		if((i = dirname(&d, name)) <= 0)
662 			continue;
663 		while(*path == '/')
664 			path++;
665 		for(end = path; *end != '\0'; end++)
666 			if(*end == '/')
667 				break;
668 		j = end - path;
669 		if(i == j && memcmp(name, path, j) == 0){
670 			fileinit(fp, fat, 0);
671 			fp->clust = dirclust(&d);
672 			fp->len = GETLONG(d.len);
673 			if(*end == 0)
674 				return 0;
675 			else if(d.attr & 0x10){
676 				fp->len = fat->clustsize * Sectsz;
677 				path = end;
678 				continue;
679 			}
680 			break;
681 		}
682 	}
683 	return -1;
684 }
685 
686 static int
conffat(Fat * fat,void * buf)687 conffat(Fat *fat, void *buf)
688 {
689 	Pbs *p = buf;
690 	uint fatsize, volsize, datasize, reserved;
691 	uint ver, dirsize, dirents, clusters;
692 
693 	if(GETSHORT(p->sectsize) != Sectsz)
694 		return -1;
695 	if(memcmp(p->type, "FAT", 3) && memcmp(((Pbs32*)buf)->type, "FAT", 3))
696 		return -1;
697 
698 	/* load values from fat */
699 	ver = 0;
700 	fatsize = GETSHORT(p->fatsize);
701 	if(fatsize == 0){
702 		fatsize = GETLONG(((Pbs32*)buf)->fatsize);
703 		ver = Fat32;
704 	}
705 	volsize = GETSHORT(p->volsize);
706 	if(volsize == 0)
707 		volsize = GETLONG(p->bigvolsize);
708 	reserved = GETSHORT(p->nreserv);
709 	dirents = GETSHORT(p->rootsize);
710 	dirsize = (dirents * Dirsz + Sectsz - 1) / Sectsz;
711 	datasize = volsize - (reserved + fatsize * p->nfats + dirsize);
712 	clusters = datasize / p->clustsize;
713 	if(ver != Fat32)
714 		if(clusters < 0xff7)
715 			ver = Fat12;
716 		else
717 			ver = Fat16;
718 
719 	/* fill FAT descriptor */
720 	fat->ver = ver;
721 	fat->dirents = dirents;
722 	fat->clustsize = p->clustsize;
723 	fat->fatlba = fat->partlba + reserved;
724 	fat->dirstart  = fat->fatlba + fatsize * p->nfats;
725 	if(ver == Fat32){
726 		fat->datalba = fat->dirstart;
727 		fat->dirstart  = GETLONG(((Pbs32*)buf)->rootclust);
728 		fat->eofmark = 0xffffff7;
729 	}else{
730 		fat->datalba = fat->dirstart + dirsize;
731 		if(ver == Fat16)
732 			fat->eofmark = 0xfff7;
733 		else
734 			fat->eofmark = 0xff7;
735 	}
736 	return 0;
737 }
738 
739 static int
findfat(Fat * fat,ulong xbase,ulong lba)740 findfat(Fat *fat, ulong xbase, ulong lba)
741 {
742 	struct {
743 		uchar status;
744 		uchar bchs[3];
745 		uchar typ;
746 		uchar echs[3];
747 		uchar lba[4];
748 		uchar len[4];
749 	} p[4];
750 	uchar buf[Sectsz];
751 	int i;
752 
753 	if(xbase == 0)
754 		xbase = lba;
755 	if(mmcread(lba, buf))
756 		return -1;
757 	if(buf[0x1fe] != 0x55 || buf[0x1ff] != 0xAA)
758 		return -1;
759 	memcpy(p, &buf[0x1be], sizeof(p));
760 	for(i=0; i<4; i++){
761 		switch(p[i].typ){
762 		case 0x05:
763 		case 0x0f:
764 		case 0x85:
765 			/* extended partitions */
766 			if(!findfat(fat, xbase, xbase + GETLONG(p[i].lba)))
767 				return 0;
768 			/* no break */
769 		case 0x00:
770 			continue;
771 		default:
772 			fat->partlba = lba + GETLONG(p[i].lba);
773 			if(mmcread(fat->partlba, buf))
774 				continue;
775 			if(!conffat(fat, buf))
776 				return 0;
777 		}
778 	}
779 	return -1;
780 }
781 
782 static int
load(Fat * fat,char * path,void * data)783 load(Fat *fat, char *path, void *data)
784 {
785 	uchar *p;
786 	File fi;
787 	int n;
788 
789 	print("%s", path);
790 	if(fatwalk(&fi, fat, path)){
791 		print(": not found\n", path);
792 		return -1;
793 	}
794 	print("...");
795 	p = data;
796 	while((n = fileread(&fi, p, Sectsz)) > 0)
797 		p += n;
798 	print("\n");
799 	return p - (uchar*)data;
800 }
801 
802 int
mmcboot(void)803 mmcboot(void)
804 {
805 	char file[Maxpath], *p;
806 	Fat fat;
807 
808 	if(mmcinit() < 0)
809 		return 0;
810 	if(findfat(&fat, 0, 0)){
811 		print("no fat\n");
812 		return 0;
813 	}
814 	memcpy(file, "9zynq", 6);
815 	memset(p = (char*)CONF, 0, CONFSIZE);
816 	p += load(&fat, "plan9.ini", p);
817 	p -= 9; /* "bootfile=" */
818 	while(--p >= (char*)CONF){
819 		while(p > (char*)CONF && p[-1] != '\n')
820 			p--;
821 		if(memcmp("bootfile=", p, 9) == 0){
822 			p += 9;
823 			memcpy(file, p, sizeof(file)-1);
824 			for(p=file; p < &file[sizeof(file)-1]; p++)
825 				if(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
826 					break;
827 			*p = '\0';
828 			break;
829 		}
830 	}
831 	return load(&fat, file, (void*)TZERO) > 0;
832 }
833