]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/pmmc.c
usbehci: catch interrupt in tsleep
[plan9front.git] / sys / src / 9 / pc / pmmc.c
1 /*
2  * pci mmc controller.
3  *
4  * initially written for X230 Ricoh MMC controller.
5  * cmdinfo[] table stolen from bcm/emmc.c, thanks richard.
6  *
7  * for sdhc documentation see: https://www.sdcard.org/
8  */
9 #include "u.h"
10 #include "../port/lib.h"
11 #include "../port/error.h"
12 #include "mem.h"
13 #include "dat.h"
14 #include "fns.h"
15 #include "io.h"
16 #include "../port/sd.h"
17
18 /* registers */
19 enum {
20         Rsdma   = 0x00,
21         Rbsize  = 0x04,
22         Rbcount = 0x06,
23         Rarg    = 0x08,
24         Rmode   = 0x0C,
25         Rcmd    = 0x0E,
26         Rresp0  = 0x10,
27         Rresp1  = 0x14,
28         Rresp2  = 0x18,
29         Rresp3  = 0x1C,
30         Rdat0   = 0x20,
31         Rdat1   = 0x22,
32         Rpres   = 0x24,
33         Rhc     = 0x28,
34         Rpwr    = 0x29,
35         Rbgc    = 0x2A,
36         Rwkc    = 0x2B,
37         Rclc    = 0x2C,
38         Rtmc    = 0x2E,
39         Rsrst   = 0x2F,
40         Rnis    = 0x30,
41         Reis    = 0x32,
42         Rnie    = 0x34,
43         Reie    = 0x36,
44         Rnise   = 0x38,
45         Reise   = 0x3A,
46         Ra12    = 0x3C,
47         Rcap    = 0x40,
48         Rrcap   = 0x44,
49         Rxcap   = 0x48,
50         Rrxcap  = 0x4C,
51         Rfea12  = 0x50,
52         Rfeei   = 0x52,
53         Radmasr = 0x54,
54         Radmaba = 0x58,
55         Rsists  = 0xFC,
56         Rhcver  = 0xFE,
57 };
58
59 /* sts bits */
60 enum {
61         Seint   = 1<<15,
62         Snint   = 1<<8,
63         Srem    = 1<<7,
64         Sins    = 1<<6,
65         Srrdy   = 1<<5,
66         Swrdy   = 1<<4,
67         Sdint   = 1<<3,
68         Sbge    = 1<<2,
69         Strac   = 1<<1,
70         Scmdc   = 1<<0,
71
72         Smask   = 0x81ff,
73 };
74
75 /* err bits */
76 enum {
77         Ea12    = 1<<8,
78         Elimit  = 1<<7,
79         Edebit  = 1<<6,
80         Edcrc   = 1<<5,
81         Edtmo   = 1<<4,
82         Ecidx   = 1<<3,
83         Ecebit  = 1<<2,
84         Eccrc   = 1<<1,
85         Ectmo   = 1<<0,
86
87         Emask   = 0x1ff,
88 };
89
90 /* present bits */
91 enum {
92         Plsig   = 1<<24,
93         Pldat3  = 1<<23,
94         Pldat2  = 1<<22,
95         Pldat1  = 1<<21,
96         Pldat0  = 1<<20,
97
98         Ppswit  = 1<<19,
99         Pcrddt  = 1<<18,
100         Pcrdst  = 1<<17,
101         Pcrdin  = 1<<16,
102
103         Pbufrd  = 1<<11,
104         Pbufwr  = 1<<10,
105         Ptrard  = 1<<9,
106         Ptrawr  = 1<<8,
107         Pdat    = 1<<2,
108
109         Pinhbc  = 1<<1,
110         Pinhbd  = 1<<0,
111 };
112
113 /* Rmode bits */
114 enum {
115         Mblk    = 1<<5,
116         Mrd     = 1<<4,
117         Mwr     = 0<<4,
118         Ma12    = 1<<2,
119         Mcnt    = 1<<1,
120         Mdma    = 1<<0,
121 };
122
123 /* command bits */
124 enum {
125         Abort           = 3<<6,
126         Isdata          = 1<<5,
127         Ixchken         = 1<<4,
128         Crcchken        = 1<<3,
129
130         Respmask        = 3,
131         Respnone        = 0,
132         Resp48busy      = 3,
133         Resp48          = 2,
134         Resp136         = 1,
135 };
136
137 /* fake for cmdinfo */
138 enum {
139         Blkcnten        = Mblk << 8,
140         Multiblock      = Mcnt << 8,
141         Card2host       = Mrd << 8,
142         Host2card       = Mwr << 8,
143 };
144
145 static int cmdinfo[64] = {
146 [0]  Ixchken,
147 [2]  Resp136,
148 [3]  Resp48 | Ixchken | Crcchken,
149 [6]  Resp48 | Ixchken | Crcchken,
150 [7]  Resp48busy | Ixchken | Crcchken,
151 [8]  Resp48 | Ixchken | Crcchken,
152 [9]  Resp136,
153 [12] Resp48busy | Ixchken | Crcchken,
154 [13] Resp48 | Ixchken | Crcchken,
155 [16] Resp48,
156 [17] Resp48 | Isdata | Card2host | Ixchken | Crcchken,
157 [18] Resp48 | Isdata | Card2host | Multiblock | Blkcnten | Ixchken | Crcchken,
158 [24] Resp48 | Isdata | Host2card | Ixchken | Crcchken,
159 [25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken,
160 [41] Resp48,
161 [55] Resp48 | Ixchken | Crcchken,
162 };
163
164 typedef struct Ctlr Ctlr;
165 struct Ctlr {
166         Lock;
167
168         Pcidev  *pdev;
169         u8int   *mmio;
170
171         int     change;
172
173         u32int  waitsts;
174         u32int  waitmsk;
175         Rendez  r;
176
177         struct {
178                 int     bcount;
179                 int     bsize;
180         } io;
181 };
182
183 static Ctlr pmmc[1];
184
185 #define CR8(c, off)     *((u8int*)(c->mmio + off))
186 #define CR16(c, off)    *((u16int*)(c->mmio + off))
187 #define CR32(c, off)    *((u32int*)(c->mmio + off))
188
189 static void
190 mmcinterrupt(Ureg*, void *arg)
191 {
192         u16int nis, eis;
193         Ctlr *c;
194
195         c = arg;
196         nis = CR16(c, Rnis);
197         if((nis & Smask) == 0)
198                 return;         /* not for us */
199
200         CR16(c, Rnis) = nis;    /* ack */
201         ilock(c);
202         eis = 0;
203         if((nis & Seint) != 0){
204                 eis = CR16(c, Reis);
205                 CR16(c, Reis) = eis;    /* ack */
206         }
207         if((nis & Snint) != 0)
208                 CR16(c, Rnie) |= Snint; /* ack */
209         if((nis & (Srem|Sins)) != 0)
210                 c->change = 1;
211         c->waitsts |= nis | (eis << 16);
212         if((c->waitsts & c->waitmsk) != 0)
213                 wakeup(&c->r);
214         iunlock(c);
215 }
216
217 static int
218 pmmcinit(void)
219 {
220         Pcidev *p;
221
222         p = nil;
223         while((p = pcimatch(p, 0, 0)) != nil){
224                 if(p->ccrb == 8 && p->ccru == 5)
225                         break;
226                 if(p->vid == 0x1180){   /* Ricoh */
227                         if(p->did == 0xe822)    /* 5U822 SD/MMC */
228                                 break;
229                         if(p->did == 0xe823)    /* 5U823 SD/MMC */
230                                 break;
231                 }
232         }
233
234         if(p == nil || p->mem[0].size < 256)
235                 return -1;
236
237         if(p->did == 0x1180 && p->vid == 0xe823){       /* Ricoh */
238                 /* Enable SD2.0 mode. */
239                 pcicfgw8(p, 0xf9, 0xfc);
240                 pcicfgw8(p, 0x150, 0x10);
241                 pcicfgw8(p, 0xf9, 0x00);
242
243                 /*
244                  * Some SD/MMC cards don't work with the default base
245                  * clock frequency of 200MHz.  Lower it to 50Hz.
246                  */
247                 pcicfgw8(p, 0xfc, 0x01);
248                 pcicfgw8(p, 0xe1, 50);
249                 pcicfgw8(p, 0xfc, 0x00);
250         }
251
252         pmmc->mmio = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
253         if(pmmc->mmio == nil)
254                 return -1;
255         pmmc->pdev = p;
256         return 0;
257 }
258
259 static int
260 pmmcinquiry(char *inquiry, int inqlen)
261 {
262         return snprint(inquiry, inqlen, "MMC Host Controller");
263 }
264
265 static void
266 softreset(Ctlr *c, int all)
267 {
268         int i, m;
269
270         m = all ? 1 : 6;
271         CR8(c, Rsrst) = m;
272         for(i=10; i>=0; i--){
273                 if((CR8(c, Rsrst) & m) == 0)
274                         break;
275                 delay(10);
276                 CR8(c, Rsrst) = 0;
277         }
278         if(i < 0) iprint("mmc: didnt reset\n");
279 }
280
281 static void
282 setpower(Ctlr *c, int on)
283 {
284         enum {
285                 Vcap18  = 1<<26,        Vset18  = 0x05,
286                 Vcap30  = 1<<25,        Vset30  = 0x06,
287                 Vcap33  = 1<<24,        Vset33  = 0x07,
288         };
289         u32int cap, v;
290
291         cap = CR32(c, Rcap);
292         v = Vset18;
293         if(cap & Vcap30)
294                 v = Vset30;
295         if(cap & Vcap33)
296                 v = Vset33;
297         CR8(c, Rpwr) = on ? ((v<<1) | 1) : 0;
298 }
299
300 static void
301 setclkfreq(Ctlr *c, int khz)
302 {
303         u32int caps, intfreq;
304         int i, div;
305
306         if(khz == 0){
307                 CR16(c, Rclc) |= ~4;    /* sd clock disable */
308                 return;
309         }
310
311         caps = CR32(c, Rcap);
312         intfreq = 1000*((caps >> 8) & 0x3f);
313         for(div = 1; div <= 256; div <<= 1){
314                 if((intfreq / div) <= khz){
315                         div >>= 1;
316                         break;
317                 }
318         }
319         CR16(c, Rclc) = 0;
320         CR16(c, Rclc) = div<<8;
321         CR16(c, Rclc) |= 1;     /* int clock enable */
322         for(i=1000; i>=0; i--){
323                 if(CR16(c, Rclc) & 2)   /* int clock stable */
324                         break;
325                 delay(10);
326         }
327         if(i < 0) iprint("mmc: clock didnt stabilize\n");
328         CR16(c, Rclc) |= 4;     /* sd clock enable */
329 }
330
331 static void
332 resetctlr(Ctlr *c)
333 {
334         u32int m;
335
336         ilock(c);
337         CR16(c, Rnise) = 0;     /* interrupts off */
338
339         c->change = 0;
340         c->waitmsk = c->waitsts = 0;
341         softreset(c, 1);
342
343         /* set timeout */
344         CR8(c, Rtmc) = 0x0e;
345
346         m = Srem | Sins | Srrdy | Swrdy | Sdint | Sbge | Strac | Scmdc;
347         CR16(c, Rnie) = m;
348         CR16(c, Reie) = Emask;
349         CR16(c, Rnise) = m;
350         CR16(c, Reise) = Emask;
351
352         setpower(c, 1); 
353         setclkfreq(c, 400);
354         iunlock(c);
355 }
356
357 static int
358 waitcond(void *arg)
359 {
360         Ctlr *c;
361
362         c = arg;
363         return (c->waitsts & c->waitmsk) != 0;
364 }
365
366 static u32int
367 intrwait(Ctlr *c, u32int mask, int tmo)
368 {
369         u32int status;
370
371         ilock(c);
372         c->waitmsk = Seint | mask;
373         iunlock(c);
374         do {
375                 if(!waserror()){
376                         tsleep(&c->r, waitcond, c, 100);
377                         poperror();
378                 }
379                 mmcinterrupt(nil, c);
380                 if(waitcond(c))
381                         break;
382                 tmo -= 100;
383         } while(tmo > 0);
384         ilock(c);
385         c->waitmsk = 0;
386         status = c->waitsts;
387         c->waitsts &= ~(status & mask);
388         if((status & mask) == 0 || (status & Seint) != 0){
389                 /* abort command on timeout/error interrupt */
390                 softreset(c, 0);
391                 c->waitsts = 0;
392                 status = 0;
393         }
394         iunlock(c);
395
396         return status & mask;
397 }
398
399
400 static void
401 pmmcenable(void)
402 {
403         Pcidev *p;
404         Ctlr *c;
405
406         c = pmmc;
407         p = c->pdev;
408         resetctlr(c);
409         intrenable(p->intl, mmcinterrupt, c, p->tbdf, "mmc");
410 }
411
412 static int
413 pmmccmd(u32int cmd, u32int arg, u32int *resp)
414 {
415         u32int status;
416         int i, mode;
417         Ctlr *c;
418
419         if(cmd >= nelem(cmdinfo) || cmdinfo[cmd] == 0)
420                 error(Egreg);
421         mode = cmdinfo[cmd] >> 8;
422         cmd = (cmd << 8) | (cmdinfo[cmd] & 0xFF);
423
424         c = pmmc;
425
426         if(c->change)
427                 resetctlr(c);
428         if((CR32(c, Rpres) & Pcrdin) == 0)
429                 error("no card");
430
431         status = Pinhbc;
432         if((cmd & Isdata) != 0 || (cmd & Respmask) == Resp48busy)
433                 status |= Pinhbd;
434         for(i=1000; (CR32(c, Rpres) & status) != 0 && i>=0; i--)
435                 delay(1);
436         if(i < 0)
437                 error(Eio);
438
439         ilock(c);
440         if((mode & (Mcnt|Mblk)) == (Mcnt|Mblk)){
441                 CR16(c, Rbsize)  = c->io.bsize;
442                 CR16(c, Rbcount) = c->io.bcount;
443         }
444         CR32(c, Rarg) = arg;
445         CR16(c, Rmode) = mode;
446         CR16(c, Rcmd) = cmd;
447         iunlock(c);
448
449         if(!intrwait(c, Scmdc, 1000))
450                 error(Eio);
451
452         switch(cmd & Respmask){
453         case Resp48busy:
454                 resp[0] = CR32(c, Rresp0);
455                 if(!intrwait(c, Strac, 3000))
456                         error(Eio);
457                 break;
458         case Resp48:
459                 resp[0] = CR32(c, Rresp0);
460                 break;
461         case Resp136:
462                 resp[0] = CR32(c, Rresp0)<<8;
463                 resp[1] = CR32(c, Rresp0)>>24 | CR32(c, Rresp1)<<8;
464                 resp[2] = CR32(c, Rresp1)>>24 | CR32(c, Rresp2)<<8;
465                 resp[3] = CR32(c, Rresp2)>>24 | CR32(c, Rresp3)<<8;
466                 break;
467         case Respnone:
468                 resp[0] = 0;
469                 break;
470         }
471
472         cmd >>= 8;
473         if(cmd == 0x06){        /* buswidth */
474                 switch(arg){
475                 case 0:
476                         CR8(c, Rhc) &= ~2;
477                         break;
478                 case 2:
479                         CR8(c, Rhc) |= 2;
480                         break;
481                 }
482         }
483
484         return 0;
485 }
486
487 static void
488 pmmciosetup(int write, void *buf, int bsize, int bcount)
489 {
490         Ctlr *c;
491
492         USED(write);
493         USED(buf);
494
495         if(bsize == 0 || (bsize & 3) != 0)
496                 error(Egreg);
497
498         c = pmmc;
499         c->io.bsize = bsize;
500         c->io.bcount = bcount;
501 }
502
503 static void
504 readblock(Ctlr *c, uchar *buf, int len)
505 {
506         for(len >>= 2; len > 0; len--){
507                 *((u32int*)buf) = CR32(c, Rdat0);
508                 buf += 4;
509         }
510 }
511
512 static void
513 writeblock(Ctlr *c, uchar *buf, int len)
514 {
515         for(len >>= 2; len > 0; len--){
516                 CR32(c, Rdat0) = *((u32int*)buf);
517                 buf += 4;
518         }
519 }
520
521 static void
522 pmmcio(int write, uchar *buf, int len)
523 {
524         Ctlr *c;
525         int n;
526
527         c = pmmc;
528         if(len != c->io.bsize*c->io.bcount)
529                 error(Egreg);
530         while(len > 0){
531                 if(!intrwait(c, write ? Swrdy : Srrdy, 3000))
532                         error(Eio);
533                 n = len;
534                 if(n > c->io.bsize)
535                         n = c->io.bsize;
536                 if(write)
537                         writeblock(c, buf, n);
538                 else
539                         readblock(c, buf, n);
540                 len -= n;
541                 buf += n;
542         }
543         if(!intrwait(c, Strac, 1000))
544                 error(Eio);
545 }
546
547 SDio sdio = {
548         "pmmc",
549         pmmcinit,
550         pmmcenable,
551         pmmcinquiry,
552         pmmccmd,
553         pmmciosetup,
554         pmmcio,
555 };