Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * kirkwood SDIO / SDMem / MMC host interface
3
 */
4
 
5
#include "u.h"
6
#include "../port/lib.h"
7
#include "../port/error.h"
8
#include "mem.h"
9
#include "dat.h"
10
#include "fns.h"
11
#include "io.h"
12
#include "../port/sd.h"
13
 
14
#define TM(bits)	((bits)<<16)
15
#define	GETTM(bits)	(((bits)>>16)&0xFFFF)
16
#define GETCMD(bits)	((bits)&0xFFFF)
17
 
18
typedef struct Ctlr Ctlr;
19
 
20
enum {
21
	Clkfreq	= 100000000,	/* external clk frequency */
22
	Initfreq= 400000,	/* initialisation frequency for MMC */
23
	SDfreq	= 25000000,	/* standard SD frequency */
24
	PIOread	= 0,		/* use programmed i/o (not dma) for reading */
25
	PIOwrite= 0,		/* use programmed i/o (not dma) writing */
26
	Polldone= 0,		/* poll for Datadone status, don't use interrupt */
27
	Pollread= 1,		/* poll for reading blocks */
28
	Pollwrite= 1,		/* poll for writing blocks */
29
 
30
	MMCSelect= 7,		/* mmc/sd card select command */
31
	Setbuswidth= 6,		/* mmc/sd set bus width command */
32
};
33
 
34
enum {
35
	/* Controller registers */
36
	DmaLSB		= 0x0>>2,
37
	DmaMSB		= 0x4>>2,
38
	Blksize		= 0x8>>2,
39
	Blkcount	= 0xc>>2,
40
	ArgLSB		= 0x10>>2,
41
	ArgMSB		= 0x14>>2,
42
	Tm		= 0x18>>2,
43
	Cmd		= 0x1c>>2,
44
	Resp0		= 0x20>>2,
45
	Resp1		= 0x24>>2,
46
	Resp2		= 0x28>>2,
47
	Resp3		= 0x2c>>2,
48
	Resp4		= 0x30>>2,
49
	Resp5		= 0x34>>2,
50
	Resp6		= 0x38>>2,
51
	Resp7		= 0x3c>>2,
52
	Data		= 0x40>>2,
53
	Hoststat	= 0x48>>2,
54
	Hostctl		= 0x50>>2,
55
	Clockctl	= 0x58>>2,
56
	Softreset	= 0x5C>>2,
57
	Interrupt	= 0x60>>2,
58
	ErrIntr		= 0x64>>2,
59
	Irptmask	= 0x68>>2,
60
	ErrIrptmask	= 0x6C>>2,
61
	Irpten		= 0x70>>2,
62
	ErrIrpten	= 0x74>>2,
63
	Mbuslo		= 0x100>>2,
64
	Mbushi		= 0x104>>2,
65
	Win0ctl		= 0x108>>2,
66
	Win0base	= 0x10c>>2,
67
	Win1ctl		= 0x110>>2,
68
	Win1base	= 0x114>>2,
69
	Win2ctl		= 0x118>>2,
70
	Win2base	= 0x11c>>2,
71
	Win3ctl		= 0x120>>2,
72
	Win3base	= 0x124>>2,
73
	Clockdiv	= 0x128>>2,
74
 
75
	/* Hostctl */
76
	Timeouten	= 1<<15,
77
	Datatoshift	= 11,
78
	Datatomask	= 0x7800,
79
	Hispeed		= 1<<10,
80
	Dwidth4		= 1<<9,
81
	Dwidth1		= 0<<9,
82
	Bigendian	= 1<<3,
83
	LSBfirst	= 1<<4,
84
	Cardtypemask	= 3<<1,
85
	Cardtypemem	= 0<<1,
86
	Cardtypeio	= 1<<1,
87
	Cardtypeiomem	= 2<<1,
88
	Cardtypsdio	= 3<<1,
89
	Pushpullen	= 1<<0,
90
 
91
	/* Clockctl */
92
	Sdclken		= 1<<0,
93
 
94
	/* Softreset */
95
	Swreset		= 1<<8,
96
 
97
	/* Cmd */
98
	Indexshift	= 8,
99
	Isdata		= 1<<5,
100
	Ixchken		= 1<<4,
101
	Crcchken	= 3<<2,
102
	Respmask	= 3<<0,
103
	Respnone	= 0<<0,
104
	Resp136		= 1<<0,
105
	Resp48		= 2<<0,
106
	Resp48busy	= 3<<0,
107
 
108
	/* Tm */
109
	Hostdma		= 0<<6,
110
	Hostpio		= 1<<6,
111
	Stopclken	= 1<<5,
112
	Host2card	= 0<<4,
113
	Card2host	= 1<<4,
114
	Autocmd12	= 1<<2,
115
	Hwwrdata	= 1<<1,
116
	Swwrdata	= 1<<0,
117
 
118
	/* ErrIntr */
119
	Crcstaterr	= 1<<14,
120
	Crcstartbiterr	= 1<<13,
121
	Crcendbiterr	= 1<<12,
122
	Resptbiterr	= 1<<11,
123
	Xfersizeerr	= 1<<10,
124
	Cmdstarterr	= 1<<9,
125
	Acmderr		= 1<<8,
126
	Denderr		= 1<<6,
127
	Dcrcerr		= 1<<5,
128
	Dtoerr		= 1<<4,
129
	Cbaderr		= 1<<3,
130
	Cenderr		= 1<<2,
131
	Ccrcerr		= 1<<1,
132
	Ctoerr		= 1<<0,
133
 
134
	/* Interrupt */
135
	Err		= 1<<15,
136
	Write8ready	= 1<<11,
137
	Read8wready	= 1<<10,
138
	Cardintr	= 1<<8,
139
	Readrdy		= 1<<5,
140
	Writerdy	= 1<<4,
141
	Dmadone		= 1<<3,
142
	Blockgap	= 1<<2,
143
	Datadone	= 1<<1,
144
	Cmddone		= 1<<0,
145
 
146
	/* Hoststat */
147
	Fifoempty	= 1<<13,
148
	Fifofull	= 1<<12,
149
	Rxactive	= 1<<9,
150
	Txactive	= 1<<8,
151
	Cardbusy	= 1<<1,
152
	Cmdinhibit	= 1<<0,
153
};
154
 
155
int cmdinfo[64] = {
156
[0]	Ixchken,
157
[2]	Resp136,
158
[3]	Resp48 | Ixchken | Crcchken,
159
[6]	Resp48 | Ixchken | Crcchken,
160
[7]	Resp48busy | Ixchken | Crcchken,
161
[8]	Resp48 | Ixchken | Crcchken,
162
[9]	Resp136,
163
[12]	Resp48busy | Ixchken | Crcchken,
164
[13]	Resp48 | Ixchken | Crcchken,
165
[16]	Resp48,
166
[17]	Resp48 | Isdata | TM(Card2host) | Ixchken | Crcchken,
167
[18]	Resp48 | Isdata | TM(Card2host) | Ixchken | Crcchken,
168
[24]	Resp48 | Isdata | TM(Host2card | Hwwrdata) | Ixchken | Crcchken,
169
[25]	Resp48 | Isdata | TM(Host2card | Hwwrdata) | Ixchken | Crcchken,
170
[41]	Resp48,
171
[55]	Resp48 | Ixchken | Crcchken,
172
};
173
 
174
struct Ctlr {
175
	Rendez	r;
176
	int	datadone;
177
	int	fastclock;
178
};
179
 
180
static Ctlr ctlr;
181
 
182
static void sdiointerrupt(Ureg*, void*);
183
 
184
void
185
WR(int reg, u32int val)
186
{
187
	u32int *r;
188
 
189
	r = (u32int*)AddrSdio;
190
	val &= 0xFFFF;
191
	if(0)iprint("WR %#4.4ux %#ux\n", reg<<2, val);
192
	r[reg] = val;
193
}
194
 
195
static uint
196
clkdiv(uint d)
197
{
198
	assert(d < 1<<11);
199
	return d;
200
}
201
 
202
static int
203
datadone(void*)
204
{
205
	return ctlr.datadone;
206
}
207
 
208
static int
209
sdioinit(void)
210
{
211
	u32int *r;
212
 
213
	r = (u32int*)AddrSdio;
214
	WR(Softreset, Swreset);
215
	while(r[Softreset] & Swreset)
216
		;
217
	delay(10);
218
	return 0;
219
}
220
 
221
static int
222
sdioinquiry(char *inquiry, int inqlen)
223
{
224
	return snprint(inquiry, inqlen, "SDIO Host Controller");
225
}
226
 
227
static void
228
sdioenable(void)
229
{
230
	u32int *r;
231
 
232
	r = (u32int*)AddrSdio;
233
	WR(Clockdiv, clkdiv(Clkfreq/Initfreq - 1));
234
	delay(10);
235
	WR(Clockctl, r[Clockctl] & ~Sdclken);
236
	WR(Hostctl, Pushpullen|Bigendian|Cardtypemem);
237
	WR(Irpten, 0);
238
	WR(Interrupt, ~0);
239
	WR(ErrIntr, ~0);
240
	WR(Irptmask, ~0);
241
	WR(ErrIrptmask, ~Dtoerr);
242
	intrenable(Irqlo, IRQ0sdio, sdiointerrupt, &ctlr, "sdio");
243
}
244
 
245
static int
246
awaitdone(u32int *r, int bits, int ticks)
247
{
248
	int i;
249
	ulong start;
250
 
251
	start = m->ticks;
252
	while(((i = r[Interrupt]) & (bits|Err)) == 0)
253
		if(m->ticks - start > ticks)
254
			break;
255
	return i;
256
}
257
 
258
static void
259
ckerr(u32int *r, int i, int len, char *op)
260
{
261
	int err;
262
 
263
	if(i & Err){
264
		err = r[ErrIntr];
265
		iprint("sdioio: (%d) %s error intr %#ux err %#ux stat %#ux\n",
266
			len, op, i, err, r[Hoststat]);
267
		WR(ErrIntr, err);
268
		WR(Interrupt, i);
269
		error(Eio);
270
	}
271
}
272
 
273
static void
274
ckdmadone(u32int *r, int i, char *msg)
275
{
276
	if((i & Dmadone) == 0){
277
		iprint("sdioio: %s intr %#ux stat %#ux\n", msg, i, r[Hoststat]);
278
		WR(Interrupt, i);
279
		error(Eio);
280
	}
281
}
282
 
283
static void
284
getresp(u32int *r, u32int *resp, int resptype)
285
{
286
	switch(resptype){
287
	case Resp136:
288
		resp[0] = r[Resp7]<<8  | r[Resp6]<<22;
289
		resp[1] = r[Resp6]>>10 | r[Resp5]<<6 | r[Resp4]<<22;
290
		resp[2] = r[Resp4]>>10 | r[Resp3]<<6 | r[Resp2]<<22;
291
		resp[3] = r[Resp2]>>10 | r[Resp1]<<6 | r[Resp0]<<22;
292
		break;
293
	case Resp48:
294
	case Resp48busy:
295
		resp[0] = r[Resp2] | r[Resp1]<<6 | r[Resp0]<<22;
296
		break;
297
	case Respnone:
298
		resp[0] = 0;
299
		break;
300
	}
301
}
302
 
303
static void
304
awaitresp48data(u32int *r, u32int cmd)
305
{
306
	int i;
307
 
308
	if(Polldone)
309
		i = awaitdone(r, Datadone, 3*HZ);
310
	else{
311
		WR(Irpten, Datadone|Err);
312
		tsleep(&ctlr.r, datadone, 0, 3000);
313
		i = ctlr.datadone;
314
		ctlr.datadone = 0;
315
		WR(Irpten, 0);
316
	}
317
	if((i & Datadone) == 0)
318
		iprint("sdioio: no Datadone after CMD%d\n", cmd);
319
	if(i & Err)
320
		iprint("sdioio: CMD%d error interrupt %#ux %#ux\n",
321
			cmd, r[Interrupt], r[ErrIntr]);
322
	WR(Interrupt, i);
323
}
324
 
325
static void
326
finishcmd(u32int cmd, u32int arg)
327
{
328
	u32int *r;
329
 
330
	/*
331
	 * Once card is selected, use faster clock.
332
	 * If card bus width changes, change host bus width.
333
	 */
334
	r = (u32int*)AddrSdio;
335
	if(cmd == MMCSelect){
336
		delay(10);
337
		WR(Clockdiv, clkdiv(Clkfreq/SDfreq - 1));
338
		delay(10);
339
		ctlr.fastclock = 1;
340
	} else if(cmd == Setbuswidth)
341
		switch(arg){
342
		case 0:
343
			WR(Hostctl, r[Hostctl] & ~Dwidth4);
344
			break;
345
		case 2:
346
			WR(Hostctl, r[Hostctl] | Dwidth4);
347
			break;
348
		}
349
}
350
 
351
static int
352
sdiocmd(u32int cmd, u32int arg, u32int *resp)
353
{
354
	int i, err;
355
	u32int c;
356
	u32int *r;
357
 
358
	assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0);
359
	i = GETTM(cmdinfo[cmd]);
360
	c = cmd<<Indexshift | GETCMD(cmdinfo[cmd]);
361
	if(c & Isdata)
362
		if(i & Card2host)
363
			i |= PIOread?  Hostpio: Hostdma;
364
		else
365
			i |= PIOwrite? Hostpio: Hostdma;
366
	WR(Tm, i);
367
	WR(ArgLSB, arg);
368
	WR(ArgMSB, arg>>16);
369
	WR(ErrIntr, ~0);
370
	WR(Cmd, c);
371
 
372
	r = (u32int*)AddrSdio;
373
	i = awaitdone(r, Cmddone, HZ);
374
	if((i & (Cmddone|Err)) != Cmddone){
375
		if((err = r[ErrIntr]) != Ctoerr)
376
			iprint("sdio: cmd %#ux error intr %#ux %#ux stat %#ux\n",
377
				c, i, err, r[Hoststat]);
378
		WR(ErrIntr, err);
379
		WR(Interrupt, i);
380
		error(Eio);
381
	}
382
	WR(Interrupt, i & ~Datadone);
383
 
384
	c &= Respmask;
385
	getresp(r, resp, c);
386
	if(c == Resp48busy)
387
		awaitresp48data(r, cmd);
388
 
389
	finishcmd(cmd, arg);
390
	return 0;
391
}
392
 
393
static void
394
sdioiosetup(int write, void *buf, int bsize, int bcount)
395
{
396
	int len;
397
	uintptr pa;
398
 
399
	pa = PADDR(buf);
400
	if(write && !PIOwrite){
401
		WR(DmaLSB, pa);
402
		WR(DmaMSB, pa>>16);
403
		len = bsize * bcount;
404
		cachedwbse(buf, len);
405
		l2cacheuwbse(buf, len);
406
	}else if(!write && !PIOread){
407
		WR(DmaLSB, pa);
408
		WR(DmaMSB, pa>>16);
409
		len = bsize * bcount;
410
		cachedwbinvse(buf, len);
411
		l2cacheuwbinvse(buf, len);
412
	}
413
	WR(Blksize, bsize);
414
	WR(Blkcount, bcount);
415
}
416
 
417
static uchar *
418
getdatas(u32int *r, uchar *buf)
419
{
420
	ushort d;
421
 
422
	d = r[Data];
423
	*buf++ = d;
424
	*buf++ = d>>8;
425
	return buf;
426
}
427
 
428
static int
429
sdioread(uchar *buf, int *lenp)
430
{
431
	int i, now, len;
432
	u32int *r;
433
 
434
	r = (u32int*)AddrSdio;
435
	i = 0;
436
	len = *lenp;
437
	while(len > 0){
438
		if(Pollread){
439
			now = m->ticks;
440
			i = awaitdone(r, Read8wready|Readrdy, 3*HZ);
441
			if(m->ticks - now > 3*HZ){
442
				print("sdioio: (%d) no Readrdy intr %#ux stat %#ux\n",
443
					len, i, r[Hoststat]);
444
				error(Eio);
445
			}
446
		}else{
447
			i = r[Interrupt];
448
			if((i & (Read8wready|Readrdy|Err)) == 0){
449
				WR(Irpten, (len > 8*4? Read8wready:
450
					Readrdy) | Err);
451
				tsleep(&ctlr.r, datadone, 0, 3000);
452
				WR(Irpten, 0);
453
				i = ctlr.datadone;
454
				ctlr.datadone = 0;
455
				if((i & (Read8wready|Readrdy|Err)) == 0){
456
					print("sdioio: (%d) no Readrdy intr %#ux stat %#ux\n",
457
						len, i, r[Hoststat]);
458
					error(Eio);
459
				}
460
			}
461
		}
462
 
463
		if((i & Read8wready) && len >= 8*2*2){
464
			for(i = 0; i < 8*2; i++)
465
				buf = getdatas(r, buf);
466
			len -= 8*2*2;
467
		}else if(i & Readrdy){
468
			buf = getdatas(r, buf);
469
			buf = getdatas(r, buf);
470
			len -= 2*2;
471
		} else
472
			ckerr(r, i, len, "read");
473
	}
474
	*lenp = len;
475
	return i;
476
}
477
 
478
static int
479
sdiowrite(uchar *buf, int *lenp)
480
{
481
	int i, now, len;
482
	u32int *r;
483
 
484
	r = (u32int*)AddrSdio;
485
	i = 0;
486
	len = *lenp;
487
	while(len > 0){
488
		if(Pollwrite){
489
			now = m->ticks;
490
			i = awaitdone(r, Writerdy, 8*HZ);
491
			if(m->ticks - now > 8*HZ){
492
				print("sdioio: (%d) no Writerdy intr %#ux stat %#ux\n",
493
					len, i, r[Hoststat]);
494
				error(Eio);
495
			}
496
		}else{
497
			i = r[Interrupt];
498
			if((i & (Writerdy|Err)) == 0){
499
				WR(Irpten, Writerdy | Err);
500
				tsleep(&ctlr.r, datadone, 0, 8000);
501
				WR(Irpten, 0);
502
				i = ctlr.datadone;
503
				ctlr.datadone = 0;
504
				if((i & (Writerdy|Err)) == 0){
505
					print("sdioio: (%d) no Writerdy intr %#ux stat %#ux\n",
506
						len, i, r[Hoststat]);
507
					error(Eio);
508
				}
509
			}
510
		}
511
		if(i & Writerdy){
512
			r[Data] = buf[0] | buf[1]<<8;
513
			r[Data] = buf[2] | buf[3]<<8;
514
			buf += 4;
515
			len -= 4;
516
		} else
517
			ckerr(r, i, len, "write");
518
	}
519
	*lenp = len;
520
	return i;
521
}
522
 
523
static void
524
sdioio(int write, uchar *buf, int len)
525
{
526
	int i;
527
	u32int *r;
528
 
529
	assert((len & 3) == 0);
530
	r = (u32int*)AddrSdio;
531
	if(write && PIOwrite)
532
		i = sdiowrite(buf, &len);
533
	else if(!write && PIOread)
534
		i = sdioread(buf, &len);
535
	else{
536
		WR(Irpten, Dmadone|Err);
537
		tsleep(&ctlr.r, datadone, 0, 3000);
538
		WR(Irpten, 0);
539
		i = ctlr.datadone;
540
		ctlr.datadone = 0;
541
		ckerr(r, i, len, "dma");
542
		ckdmadone(r, i, "no dma done");
543
		WR(Interrupt, Dmadone);
544
	}
545
 
546
	if(Polldone)
547
		i = awaitdone(r, Datadone, 3*HZ);
548
	else if((i & Datadone) == 0){
549
		WR(Irpten, Datadone|Err);
550
		tsleep(&ctlr.r, datadone, 0, 3000);
551
		i = ctlr.datadone;
552
		ctlr.datadone = 0;
553
		WR(Irpten, 0);
554
	}
555
	ckerr(r, i, len, "IO");
556
	ckdmadone(r, i, "IO timeout");
557
	if(i)
558
		WR(Interrupt, i);
559
}
560
 
561
static void
562
sdiointerrupt(Ureg*, void*)
563
{
564
	u32int *r;
565
 
566
	r = (u32int*)AddrSdio;
567
	ctlr.datadone = r[Interrupt];
568
	WR(Irpten, 0);
569
	wakeup(&ctlr.r);
570
}
571
 
572
SDio sdio = {
573
	"sdio",
574
	sdioinit,
575
	sdioenable,
576
	sdioinquiry,
577
	sdiocmd,
578
	sdioiosetup,
579
	sdioio,
580
};