xref: /plan9-contrib/sys/src/nboot/zynq/mmc.c (revision 529c1f209803c78c4f2cda11b13818a57f01c872)
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