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