]> git.lizzy.rs Git - plan9front.git/blob - sys/src/boot/zynq/mmc.c
ip/cifsd: fix missing int return type for vpack() (thanks pr)
[plan9front.git] / sys / src / boot / zynq / mmc.c
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
281 GETSHORT(void *v)
282 {
283         uchar *p = v;
284         return p[0] | p[1]<<8;
285 }
286 static ulong
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
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
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
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
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
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
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
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
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
565 dirclust(Dir *d)
566 {
567         return GETSHORT(d->starthi)<<16 | GETSHORT(d->startlo);
568 }
569
570 static void
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
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
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
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
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
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
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
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 }