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
WR(int reg,u32int val)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
clkdiv(uint d)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
datadone(void *)179 datadone(void*)
180 {
181 return emmc.datadone;
182 }
183
184 static int
emmcinit(void)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
emmcinquiry(char * inquiry,int inqlen)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
emmcenable(void)223 emmcenable(void)
224 {
225 u32int *r;
226 int i;
227
228 r = (u32int*)EMMCREGS;
229 WR(Control1, clkdiv(emmc.extclk/Initfreq - 1) |
230 DTO<<Datatoshift | 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
emmccmd(u32int cmd,u32int arg,u32int * resp)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((r[Status] & Datinhibit) &&
263 ((c & Isdata) || (c & Respmask) == Resp48busy)){
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 }
351 return 0;
352 }
353
354 void
emmciosetup(int write,void * buf,int bsize,int bcount)355 emmciosetup(int write, void *buf, int bsize, int bcount)
356 {
357 USED(write);
358 USED(buf);
359 WR(Blksizecnt, bcount<<16 | bsize);
360 }
361
362 static void
emmcio(int write,uchar * buf,int len)363 emmcio(int write, uchar *buf, int len)
364 {
365 u32int *r;
366 int i;
367
368 r = (u32int*)EMMCREGS;
369 assert((len&3) == 0);
370 okay(1);
371 if(waserror()){
372 okay(0);
373 nexterror();
374 }
375 if(write)
376 dmastart(DmaChanEmmc, DmaDevEmmc, DmaM2D,
377 buf, &r[Data], len);
378 else
379 dmastart(DmaChanEmmc, DmaDevEmmc, DmaD2M,
380 &r[Data], buf, len);
381 if(dmawait(DmaChanEmmc) < 0)
382 error(Eio);
383 WR(Irpten, Datadone|Err);
384 tsleep(&emmc.r, datadone, 0, 3000);
385 i = emmc.datadone;
386 emmc.datadone = 0;
387 WR(Irpten, 0);
388 if((i & Datadone) == 0){
389 print("emmcio: %d timeout intr %ux stat %ux\n",
390 write, i, r[Status]);
391 WR(Interrupt, i);
392 error(Eio);
393 }
394 if(i & Err){
395 print("emmcio: %d error intr %ux stat %ux\n",
396 write, r[Interrupt], r[Status]);
397 WR(Interrupt, i);
398 error(Eio);
399 }
400 if(i)
401 WR(Interrupt, i);
402 poperror();
403 okay(0);
404 }
405
406 static void
mmcinterrupt(Ureg *,void *)407 mmcinterrupt(Ureg*, void*)
408 {
409 u32int *r;
410 int i;
411
412 r = (u32int*)EMMCREGS;
413 i = r[Interrupt];
414 r[Interrupt] = i & (Datadone|Err);
415 emmc.datadone = i;
416 wakeup(&emmc.r);
417 }
418
419 SDio sdio = {
420 "emmc",
421 emmcinit,
422 emmcenable,
423 emmcinquiry,
424 emmccmd,
425 emmciosetup,
426 emmcio,
427 };
428