xref: /plan9/sys/src/9/kw/sdio.c (revision 0cc6832d7c845a6250c7353daab06d4af2dfe5cb)
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
WR(int reg,u32int val)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
clkdiv(uint d)196 clkdiv(uint d)
197 {
198 	assert(d < 1<<11);
199 	return d;
200 }
201 
202 static int
datadone(void *)203 datadone(void*)
204 {
205 	return ctlr.datadone;
206 }
207 
208 static int
sdioinit(void)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
sdioinquiry(char * inquiry,int inqlen)222 sdioinquiry(char *inquiry, int inqlen)
223 {
224 	return snprint(inquiry, inqlen, "SDIO Host Controller");
225 }
226 
227 static void
sdioenable(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
awaitdone(u32int * r,int bits,int ticks)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
ckerr(u32int * r,int i,int len,char * op)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
ckdmadone(u32int * r,int i,char * msg)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
getresp(u32int * r,u32int * resp,int resptype)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
awaitresp48data(u32int * r,u32int cmd)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
finishcmd(u32int cmd,u32int arg)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
sdiocmd(u32int cmd,u32int arg,u32int * resp)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
sdioiosetup(int write,void * buf,int bsize,int bcount)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 *
getdatas(u32int * r,uchar * buf)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
sdioread(uchar * buf,int * lenp)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
sdiowrite(uchar * buf,int * lenp)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
sdioio(int write,uchar * buf,int len)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
sdiointerrupt(Ureg *,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 };
581