]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm/emmc.c
kernel: reduce Page structure size by changing Page.cachectl[]
[plan9front.git] / sys / src / 9 / bcm / emmc.c
1 /*
2  * bcm2835 external mass media controller (mmc / sd host interface)
3  *
4  * Copyright © 2012 Richard Miller <r.miller@acm.org>
5  */
6
7 #include "u.h"
8 #include "../port/lib.h"
9 #include "../port/error.h"
10 #include "mem.h"
11 #include "dat.h"
12 #include "fns.h"
13 #include "io.h"
14 #include "../port/sd.h"
15
16 #define EMMCREGS        (VIRTIO+0x300000)
17
18 enum {
19         Extfreq         = 100*Mhz,      /* guess external clock frequency if */
20                                         /* not available from vcore */
21         Initfreq        = 400000,       /* initialisation frequency for MMC */
22         SDfreq          = 25*Mhz,       /* standard SD frequency */
23         DTO             = 14,           /* data timeout exponent (guesswork) */
24
25         MMCSelect       = 7,            /* mmc/sd card select command */
26         Setbuswidth     = 6,            /* mmc/sd set bus width command */
27 };
28
29 enum {
30         /* Controller registers */
31         Arg2                    = 0x00>>2,
32         Blksizecnt              = 0x04>>2,
33         Arg1                    = 0x08>>2,
34         Cmdtm                   = 0x0c>>2,
35         Resp0                   = 0x10>>2,
36         Resp1                   = 0x14>>2,
37         Resp2                   = 0x18>>2,
38         Resp3                   = 0x1c>>2,
39         Data                    = 0x20>>2,
40         Status                  = 0x24>>2,
41         Control0                = 0x28>>2,
42         Control1                = 0x2c>>2,
43         Interrupt               = 0x30>>2,
44         Irptmask                = 0x34>>2,
45         Irpten                  = 0x38>>2,
46         Control2                = 0x3c>>2,
47         Forceirpt               = 0x50>>2,
48         Boottimeout             = 0x70>>2,
49         Dbgsel                  = 0x74>>2,
50         Exrdfifocfg             = 0x80>>2,
51         Exrdfifoen              = 0x84>>2,
52         Tunestep                = 0x88>>2,
53         Tunestepsstd            = 0x8c>>2,
54         Tunestepsddr            = 0x90>>2,
55         Spiintspt               = 0xf0>>2,
56         Slotisrver              = 0xfc>>2,
57
58         /* Control0 */
59         Dwidth4                 = 1<<1,
60         Dwidth1                 = 0<<1,
61
62         /* Control1 */
63         Srstdata                = 1<<26,        /* reset data circuit */
64         Srstcmd                 = 1<<25,        /* reset command circuit */
65         Srsthc                  = 1<<24,        /* reset complete host controller */
66         Datatoshift             = 16,           /* data timeout unit exponent */
67         Datatomask              = 0xF0000,
68         Clkfreq8shift           = 8,            /* SD clock base divider LSBs */
69         Clkfreq8mask            = 0xFF00,
70         Clkfreqms2shift         = 6,            /* SD clock base divider MSBs */
71         Clkfreqms2mask          = 0xC0,
72         Clkgendiv               = 0<<5,         /* SD clock divided */
73         Clkgenprog              = 1<<5,         /* SD clock programmable */
74         Clken                   = 1<<2,         /* SD clock enable */
75         Clkstable               = 1<<1, 
76         Clkintlen               = 1<<0,         /* enable internal EMMC clocks */
77
78         /* Cmdtm */
79         Indexshift              = 24,
80         Suspend                 = 1<<22,
81         Resume                  = 2<<22,
82         Abort                   = 3<<22,
83         Isdata                  = 1<<21,
84         Ixchken                 = 1<<20,
85         Crcchken                = 1<<19,
86         Respmask                = 3<<16,
87         Respnone                = 0<<16,
88         Resp136                 = 1<<16,
89         Resp48                  = 2<<16,
90         Resp48busy              = 3<<16,
91         Multiblock              = 1<<5,
92         Host2card               = 0<<4,
93         Card2host               = 1<<4,
94         Autocmd12               = 1<<2,
95         Autocmd23               = 2<<2,
96         Blkcnten                = 1<<1,
97
98         /* Interrupt */
99         Acmderr         = 1<<24,
100         Denderr         = 1<<22,
101         Dcrcerr         = 1<<21,
102         Dtoerr          = 1<<20,
103         Cbaderr         = 1<<19,
104         Cenderr         = 1<<18,
105         Ccrcerr         = 1<<17,
106         Ctoerr          = 1<<16,
107         Err             = 1<<15,
108         Cardintr        = 1<<8,         /* not in Broadcom datasheet */
109         Cardinsert      = 1<<6,         /* not in Broadcom datasheet */
110         Readrdy         = 1<<5,
111         Writerdy        = 1<<4,
112         Datadone        = 1<<1,
113         Cmddone         = 1<<0,
114
115         /* Status */
116         Bufread         = 1<<11,        /* not in Broadcom datasheet */
117         Bufwrite        = 1<<10,        /* not in Broadcom datasheet */
118         Readtrans       = 1<<9,
119         Writetrans      = 1<<8,
120         Datactive       = 1<<2,
121         Datinhibit      = 1<<1,
122         Cmdinhibit      = 1<<0,
123 };
124
125 int cmdinfo[64] = {
126 [0]  Ixchken,
127 [2]  Resp136,
128 [3]  Resp48 | Ixchken | Crcchken,
129 [6]  Resp48 | Ixchken | Crcchken,
130 [7]  Resp48busy | Ixchken | Crcchken,
131 [8]  Resp48 | Ixchken | Crcchken,
132 [9]  Resp136,
133 [12] Resp48busy | Ixchken | Crcchken,
134 [13] Resp48 | Ixchken | Crcchken,
135 [16] Resp48,
136 [17] Resp48 | Isdata | Card2host | Ixchken | Crcchken,
137 [18] Resp48 | Isdata | Card2host | Multiblock | Blkcnten | Ixchken | Crcchken,
138 [24] Resp48 | Isdata | Host2card | Ixchken | Crcchken,
139 [25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken,
140 [41] Resp48,
141 [55] Resp48 | Ixchken | Crcchken,
142 };
143
144 typedef struct Ctlr Ctlr;
145
146 struct Ctlr {
147         Rendez  r;
148         int     datadone;
149         int     fastclock;
150         ulong   extclk;
151 };
152
153 static Ctlr emmc;
154
155 static void mmcinterrupt(Ureg*, void*);
156
157 static void
158 WR(int reg, u32int val)
159 {
160         u32int *r = (u32int*)EMMCREGS;
161
162         if(0)print("WR %2.2ux %ux\n", reg<<2, val);
163         microdelay(emmc.fastclock? 2: 20);
164         r[reg] = val;
165 }
166
167 static uint
168 clkdiv(uint d)
169 {
170         uint v;
171
172         assert(d < 1<<10);
173         v = (d << Clkfreq8shift) & Clkfreq8mask;
174         v |= ((d >> 8) << Clkfreqms2shift) & Clkfreqms2mask;
175         return v;
176 }
177
178 static int
179 datadone(void*)
180 {
181         return emmc.datadone;
182 }
183
184 static int
185 emmcinit(void)
186 {
187         u32int *r;
188         ulong clk;
189         char *s;
190
191         clk = getclkrate(ClkEmmc);
192         s = "";
193         if(clk == 0){
194                 s = "Assuming ";
195                 clk = Extfreq;
196         }
197         emmc.extclk = clk;
198         print("%seMMC external clock %lud Mhz\n", s, clk/1000000);
199         r = (u32int*)EMMCREGS;
200         if(0)print("emmc control %8.8ux %8.8ux %8.8ux\n",
201                 r[Control0], r[Control1], r[Control2]);
202         WR(Control1, Srsthc);
203         delay(10);
204         while(r[Control1] & Srsthc)
205                 ;
206         return 0;
207 }
208
209 static int
210 emmcinquiry(char *inquiry, int inqlen)
211 {
212         u32int *r;
213         uint ver;
214
215         r = (u32int*)EMMCREGS;
216         ver = r[Slotisrver] >> 16;
217         return snprint(inquiry, inqlen,
218                 "Arasan eMMC SD Host Controller %2.2x Version %2.2x",
219                 ver&0xFF, ver>>8);
220 }
221
222 static void
223 emmcenable(void)
224 {
225         u32int *r;
226         int i;
227
228         r = (u32int*)EMMCREGS;
229         WR(Control1, clkdiv(emmc.extclk / Initfreq - 1) | DTO << Datatoshift |
230                 Clkgendiv | Clken | Clkintlen);
231         for(i = 0; i < 1000; i++){
232                 delay(1);
233                 if(r[Control1] & Clkstable)
234                         break;
235         }
236         if(i == 1000)
237                 print("SD clock won't initialise!\n");
238         WR(Irptmask, ~(Dtoerr|Cardintr));
239         intrenable(IRQmmc, mmcinterrupt, nil, 0, "mmc");
240 }
241
242 static int
243 emmccmd(u32int cmd, u32int arg, u32int *resp)
244 {
245         u32int *r;
246         u32int c;
247         int i;
248         ulong now;
249
250         r = (u32int*)EMMCREGS;
251         assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0);
252         c = (cmd << Indexshift) | cmdinfo[cmd];
253         if(r[Status] & Cmdinhibit){
254                 print("emmccmd: need to reset Cmdinhibit intr %ux stat %ux\n",
255                         r[Interrupt], r[Status]);
256                 WR(Control1, r[Control1] | Srstcmd);
257                 while(r[Control1] & Srstcmd)
258                         ;
259                 while(r[Status] & Cmdinhibit)
260                         ;
261         }
262         if((c & Isdata || (c & Respmask) == Resp48busy) &&
263             r[Status] & Datinhibit){
264                 print("emmccmd: need to reset Datinhibit intr %ux stat %ux\n",
265                         r[Interrupt], r[Status]);
266                 WR(Control1, r[Control1] | Srstdata);
267                 while(r[Control1] & Srstdata)
268                         ;
269                 while(r[Status] & Datinhibit)
270                         ;
271         }
272         WR(Arg1, arg);
273         if((i = r[Interrupt]) != 0){
274                 if(i != Cardinsert)
275                         print("emmc: before command, intr was %ux\n", i);
276                 WR(Interrupt, i);
277         }
278         WR(Cmdtm, c);
279         now = m->ticks;
280         while(((i=r[Interrupt])&(Cmddone|Err)) == 0)
281                 if(m->ticks-now > HZ)
282                         break;
283         if((i&(Cmddone|Err)) != Cmddone){
284                 if((i&~Err) != Ctoerr)
285                         print("emmc: cmd %ux error intr %ux stat %ux\n", c, i, r[Status]);
286                 WR(Interrupt, i);
287                 if(r[Status]&Cmdinhibit){
288                         WR(Control1, r[Control1]|Srstcmd);
289                         while(r[Control1]&Srstcmd)
290                                 ;
291                 }
292                 error(Eio);
293         }
294         WR(Interrupt, i & ~(Datadone|Readrdy|Writerdy));
295         switch(c & Respmask){
296         case Resp136:
297                 resp[0] = r[Resp0]<<8;
298                 resp[1] = r[Resp0]>>24 | r[Resp1]<<8;
299                 resp[2] = r[Resp1]>>24 | r[Resp2]<<8;
300                 resp[3] = r[Resp2]>>24 | r[Resp3]<<8;
301                 break;
302         case Resp48:
303         case Resp48busy:
304                 resp[0] = r[Resp0];
305                 break;
306         case Respnone:
307                 resp[0] = 0;
308                 break;
309         }
310         if((c & Respmask) == Resp48busy){
311                 WR(Irpten, Datadone|Err);
312                 tsleep(&emmc.r, datadone, 0, 3000);
313                 i = emmc.datadone;
314                 emmc.datadone = 0;
315                 WR(Irpten, 0);
316                 if((i & Datadone) == 0)
317                         print("emmcio: no Datadone after CMD%d\n", cmd);
318                 if(i & Err)
319                         print("emmcio: CMD%d error interrupt %ux\n",
320                                 cmd, r[Interrupt]);
321                 WR(Interrupt, i);
322         }
323         /*
324          * Once card is selected, use faster clock
325          */
326         if(cmd == MMCSelect){
327                 delay(10);
328                 WR(Control1, clkdiv(emmc.extclk / SDfreq - 1) |
329                         DTO << Datatoshift | Clkgendiv | Clken | Clkintlen);
330                 for(i = 0; i < 1000; i++){
331                         delay(1);
332                         if(r[Control1] & Clkstable)
333                                 break;
334                 }
335                 delay(10);
336                 emmc.fastclock = 1;
337         }
338         /*
339          * If card bus width changes, change host bus width
340          */
341         if(cmd == Setbuswidth)
342                 switch(arg){
343                 case 0:
344                         WR(Control0, r[Control0] & ~Dwidth4);
345                         break;
346                 case 2:
347                         WR(Control0, r[Control0] | Dwidth4);
348                         break;
349                 }
350         return 0;
351 }
352
353 void
354 emmciosetup(int write, void *buf, int bsize, int bcount)
355 {
356         USED(write);
357         USED(buf);
358         WR(Blksizecnt, bcount<<16 | bsize);
359 }
360
361 static void
362 emmcio(int write, uchar *buf, int len)
363 {
364         u32int *r;
365         int i;
366
367         r = (u32int*)EMMCREGS;
368         assert((len&3) == 0);
369         okay(1);
370         if(waserror()){
371                 okay(0);
372                 nexterror();
373         }
374         if(write)
375                 dmastart(DmaChanEmmc, DmaDevEmmc, DmaM2D,
376                         buf, &r[Data], len);
377         else
378                 dmastart(DmaChanEmmc, DmaDevEmmc, DmaD2M,
379                         &r[Data], buf, len);
380         if(dmawait(DmaChanEmmc) < 0)
381                 error(Eio);
382         WR(Irpten, Datadone|Err);
383         tsleep(&emmc.r, datadone, 0, 3000);
384         i = emmc.datadone;
385         emmc.datadone = 0;
386         WR(Irpten, 0);
387         if((i & Datadone) == 0){
388                 print("emmcio: %d timeout intr %ux stat %ux\n",
389                         write, i, r[Status]);
390                 WR(Interrupt, i);
391                 error(Eio);
392         }
393         if(i & Err){
394                 print("emmcio: %d error intr %ux stat %ux\n",
395                         write, r[Interrupt], r[Status]);
396                 WR(Interrupt, i);
397                 error(Eio);
398         }
399         if(i)
400                 WR(Interrupt, i);
401         poperror();
402         okay(0);
403 }
404
405 static void
406 mmcinterrupt(Ureg*, void*)
407 {       
408         u32int *r;
409         int i;
410
411         r = (u32int*)EMMCREGS;
412         i = r[Interrupt];
413         r[Interrupt] = i & (Datadone|Err);
414         emmc.datadone = i;
415         wakeup(&emmc.r);
416 }
417
418 SDio sdio = {
419         "emmc",
420         emmcinit,
421         emmcenable,
422         emmcinquiry,
423         emmccmd,
424         emmciosetup,
425         emmcio,
426 };