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