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