]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/pmmc.c
kernel: cleanup the software mouse cursor mess
[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         pmmc->mmio = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
238         if(pmmc->mmio == nil)
239                 return -1;
240
241         pmmc->pdev = p;
242         pcienable(p);
243
244         if(p->did == 0x1180 && p->vid == 0xe823){       /* Ricoh */
245                 /* Enable SD2.0 mode. */
246                 pcicfgw8(p, 0xf9, 0xfc);
247                 pcicfgw8(p, 0x150, 0x10);
248                 pcicfgw8(p, 0xf9, 0x00);
249
250                 /*
251                  * Some SD/MMC cards don't work with the default base
252                  * clock frequency of 200MHz.  Lower it to 50Hz.
253                  */
254                 pcicfgw8(p, 0xfc, 0x01);
255                 pcicfgw8(p, 0xe1, 50);
256                 pcicfgw8(p, 0xfc, 0x00);
257         }
258
259         return 0;
260 }
261
262 static int
263 pmmcinquiry(char *inquiry, int inqlen)
264 {
265         return snprint(inquiry, inqlen, "MMC Host Controller");
266 }
267
268 static void
269 softreset(Ctlr *c, int all)
270 {
271         int i, m;
272
273         m = all ? 1 : 6;
274         CR8(c, Rsrst) = m;
275         for(i=10; i>=0; i--){
276                 if((CR8(c, Rsrst) & m) == 0)
277                         break;
278                 delay(10);
279                 CR8(c, Rsrst) = 0;
280         }
281         if(i < 0) iprint("mmc: didnt reset\n");
282 }
283
284 static void
285 setpower(Ctlr *c, int on)
286 {
287         enum {
288                 Vcap18  = 1<<26,        Vset18  = 0x05,
289                 Vcap30  = 1<<25,        Vset30  = 0x06,
290                 Vcap33  = 1<<24,        Vset33  = 0x07,
291         };
292         u32int cap, v;
293
294         cap = CR32(c, Rcap);
295         v = Vset18;
296         if(cap & Vcap30)
297                 v = Vset30;
298         if(cap & Vcap33)
299                 v = Vset33;
300         CR8(c, Rpwr) = on ? ((v<<1) | 1) : 0;
301 }
302
303 static void
304 setclkfreq(Ctlr *c, int khz)
305 {
306         u32int caps, intfreq;
307         int i, div;
308
309         if(khz == 0){
310                 CR16(c, Rclc) |= ~4;    /* sd clock disable */
311                 return;
312         }
313
314         caps = CR32(c, Rcap);
315         intfreq = 1000*((caps >> 8) & 0x3f);
316         for(div = 1; div <= 256; div <<= 1){
317                 if((intfreq / div) <= khz){
318                         div >>= 1;
319                         break;
320                 }
321         }
322         CR16(c, Rclc) = 0;
323         CR16(c, Rclc) = div<<8;
324         CR16(c, Rclc) |= 1;     /* int clock enable */
325         for(i=1000; i>=0; i--){
326                 if(CR16(c, Rclc) & 2)   /* int clock stable */
327                         break;
328                 delay(10);
329         }
330         if(i < 0) iprint("mmc: clock didnt stabilize\n");
331         CR16(c, Rclc) |= 4;     /* sd clock enable */
332 }
333
334 static void
335 resetctlr(Ctlr *c)
336 {
337         u32int m;
338
339         ilock(c);
340         CR16(c, Rnise) = 0;     /* interrupts off */
341
342         c->change = 0;
343         c->waitmsk = c->waitsts = 0;
344         softreset(c, 1);
345
346         /* set timeout */
347         CR8(c, Rtmc) = 0x0e;
348
349         m = Srem | Sins | Srrdy | Swrdy | Sdint | Sbge | Strac | Scmdc;
350         CR16(c, Rnie) = m;
351         CR16(c, Reie) = Emask;
352         CR16(c, Rnise) = m;
353         CR16(c, Reise) = Emask;
354
355         setpower(c, 1); 
356         setclkfreq(c, 400);
357         iunlock(c);
358 }
359
360 static int
361 waitcond(void *arg)
362 {
363         Ctlr *c;
364
365         c = arg;
366         return (c->waitsts & c->waitmsk) != 0;
367 }
368
369 static u32int
370 intrwait(Ctlr *c, u32int mask, int tmo)
371 {
372         u32int status;
373
374         ilock(c);
375         c->waitmsk = Seint | mask;
376         iunlock(c);
377         do {
378                 if(!waserror()){
379                         tsleep(&c->r, waitcond, c, 100);
380                         poperror();
381                 }
382                 mmcinterrupt(nil, c);
383                 if(waitcond(c))
384                         break;
385                 tmo -= 100;
386         } while(tmo > 0);
387         ilock(c);
388         c->waitmsk = 0;
389         status = c->waitsts;
390         c->waitsts &= ~(status & mask);
391         if((status & mask) == 0 || (status & Seint) != 0){
392                 /* abort command on timeout/error interrupt */
393                 softreset(c, 0);
394                 c->waitsts = 0;
395                 status = 0;
396         }
397         iunlock(c);
398
399         return status & mask;
400 }
401
402
403 static void
404 pmmcenable(void)
405 {
406         Pcidev *p;
407         Ctlr *c;
408
409         c = pmmc;
410         p = c->pdev;
411         resetctlr(c);
412         intrenable(p->intl, mmcinterrupt, c, p->tbdf, "mmc");
413 }
414
415 static int
416 pmmccmd(u32int cmd, u32int arg, u32int *resp)
417 {
418         u32int status;
419         int i, mode;
420         Ctlr *c;
421
422         if(cmd >= nelem(cmdinfo) || cmdinfo[cmd] == 0)
423                 error(Egreg);
424         mode = cmdinfo[cmd] >> 8;
425         cmd = (cmd << 8) | (cmdinfo[cmd] & 0xFF);
426
427         c = pmmc;
428
429         if(c->change)
430                 resetctlr(c);
431         if((CR32(c, Rpres) & Pcrdin) == 0)
432                 error("no card");
433
434         status = Pinhbc;
435         if((cmd & Isdata) != 0 || (cmd & Respmask) == Resp48busy)
436                 status |= Pinhbd;
437         for(i=1000; (CR32(c, Rpres) & status) != 0 && i>=0; i--)
438                 delay(1);
439         if(i < 0)
440                 error(Eio);
441
442         ilock(c);
443         if((mode & (Mcnt|Mblk)) == (Mcnt|Mblk)){
444                 CR16(c, Rbsize)  = c->io.bsize;
445                 CR16(c, Rbcount) = c->io.bcount;
446         }
447         CR32(c, Rarg) = arg;
448         CR16(c, Rmode) = mode;
449         CR16(c, Rcmd) = cmd;
450         iunlock(c);
451
452         if(!intrwait(c, Scmdc, 1000))
453                 error(Eio);
454
455         switch(cmd & Respmask){
456         case Resp48busy:
457                 resp[0] = CR32(c, Rresp0);
458                 if(!intrwait(c, Strac, 3000))
459                         error(Eio);
460                 break;
461         case Resp48:
462                 resp[0] = CR32(c, Rresp0);
463                 break;
464         case Resp136:
465                 resp[0] = CR32(c, Rresp0)<<8;
466                 resp[1] = CR32(c, Rresp0)>>24 | CR32(c, Rresp1)<<8;
467                 resp[2] = CR32(c, Rresp1)>>24 | CR32(c, Rresp2)<<8;
468                 resp[3] = CR32(c, Rresp2)>>24 | CR32(c, Rresp3)<<8;
469                 break;
470         case Respnone:
471                 resp[0] = 0;
472                 break;
473         }
474
475         cmd >>= 8;
476         if(cmd == 0x06){        /* buswidth */
477                 switch(arg){
478                 case 0:
479                         CR8(c, Rhc) &= ~2;
480                         break;
481                 case 2:
482                         CR8(c, Rhc) |= 2;
483                         break;
484                 }
485         }
486
487         return 0;
488 }
489
490 static void
491 pmmciosetup(int write, void *buf, int bsize, int bcount)
492 {
493         Ctlr *c;
494
495         USED(write);
496         USED(buf);
497
498         if(bsize == 0 || (bsize & 3) != 0)
499                 error(Egreg);
500
501         c = pmmc;
502         c->io.bsize = bsize;
503         c->io.bcount = bcount;
504 }
505
506 static void
507 readblock(Ctlr *c, uchar *buf, int len)
508 {
509         for(len >>= 2; len > 0; len--){
510                 *((u32int*)buf) = CR32(c, Rdat0);
511                 buf += 4;
512         }
513 }
514
515 static void
516 writeblock(Ctlr *c, uchar *buf, int len)
517 {
518         for(len >>= 2; len > 0; len--){
519                 CR32(c, Rdat0) = *((u32int*)buf);
520                 buf += 4;
521         }
522 }
523
524 static void
525 pmmcio(int write, uchar *buf, int len)
526 {
527         Ctlr *c;
528         int n;
529
530         c = pmmc;
531         if(len != c->io.bsize*c->io.bcount)
532                 error(Egreg);
533         while(len > 0){
534                 if(!intrwait(c, write ? Swrdy : Srrdy, 3000))
535                         error(Eio);
536                 n = len;
537                 if(n > c->io.bsize)
538                         n = c->io.bsize;
539                 if(write)
540                         writeblock(c, buf, n);
541                 else
542                         readblock(c, buf, n);
543                 len -= n;
544                 buf += n;
545         }
546         if(!intrwait(c, Strac, 1000))
547                 error(Eio);
548 }
549
550 SDio sdio = {
551         "pmmc",
552         pmmcinit,
553         pmmcenable,
554         pmmcinquiry,
555         pmmccmd,
556         pmmciosetup,
557         pmmcio,
558 };