2 * external mass media controller (mmc / sd host interface)
4 * derived from Richard Miller's bcm/emmc.c
8 #include "../port/lib.h"
9 #include "../port/error.h"
14 #include "../port/sd.h"
17 Initfreq = 400000, /* initialisation frequency for MMC */
18 SDfreq = 25000000, /* standard SD frequency */
19 DTO = 14, /* data timeout exponent (guesswork) */
21 MMCSelect = 7, /* mmc/sd card select command */
22 Setbuswidth = 6, /* mmc/sd set bus width command */
26 /* Controller registers */
42 Capabilites = 0x40>>2,
44 Boottimeout = 0x60>>2,
54 Srstdata = 1<<26, /* reset data circuit */
55 Srstcmd = 1<<25, /* reset command circuit */
56 Srsthc = 1<<24, /* reset complete host controller */
57 Datatoshift = 16, /* data timeout unit exponent */
59 Clkfreq8shift = 8, /* SD clock base divider LSBs */
60 Clkfreq8mask = 0xFF00,
61 Clkfreqms2shift = 6, /* SD clock base divider MSBs */
62 Clkfreqms2mask = 0xC0,
63 Clkgendiv = 0<<5, /* SD clock divided */
64 Clkgenprog = 1<<5, /* SD clock programmable */
65 Clken = 1<<2, /* SD clock enable */
67 Clkintlen = 1<<0, /* enable internal EMMC clocks */
119 static int cmdinfo[64] = {
122 [3] Resp48 | Ixchken | Crcchken,
123 [6] Resp48 | Ixchken | Crcchken,
124 [7] Resp48busy | Ixchken | Crcchken,
125 [8] Resp48 | Ixchken | Crcchken,
127 [12] Resp48busy | Ixchken | Crcchken,
128 [13] Resp48 | Ixchken | Crcchken,
130 [17] Resp48 | Isdata | Card2host | Ixchken | Crcchken | Dmaen,
131 [18] Resp48 | Isdata | Card2host | Multiblock | Blkcnten | Ixchken | Crcchken | Dmaen,
132 [24] Resp48 | Isdata | Host2card | Ixchken | Crcchken | Dmaen,
133 [25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken | Dmaen,
135 [55] Resp48 | Ixchken | Crcchken,
138 typedef struct Ctlr Ctlr;
156 v = (d << Clkfreq8shift) & Clkfreq8mask;
157 v |= ((d >> 8) << Clkfreqms2shift) & Clkfreqms2mask;
162 interrupt(Ureg*, void*)
169 r[Interrupt] = i & (Datadone|Err);
177 return emmc.datadone;
186 emmc.extclk = 100000000;
188 r = vmap(SDIO_BASE, 0x100);
190 r[Control1] = Srsthc;
191 for(i = 0; i < 100; i++){
193 if((r[Control1] & Srsthc) == 0)
196 print("emmc: reset timeout!\n");
201 emmcinquiry(char *inquiry, int inqlen)
205 ver = emmc.regs[Slotisrver] >> 16;
206 return snprint(inquiry, inqlen,
207 "eMMC SD Host Controller %2.2x Version %2.2x",
216 emmc.regs[Control1] = clkdiv(emmc.extclk / Initfreq - 1) | DTO << Datatoshift |
217 Clkgendiv | Clken | Clkintlen;
218 for(i = 0; i < 1000; i++){
220 if(emmc.regs[Control1] & Clkstable)
224 print("SD clock won't initialise!\n");
225 emmc.regs[Irptmask] = ~(Dtoerr|Cardintr|Dmaintr);
226 intrenable(emmc.irq, interrupt, nil, LEVEL, sdio.name);
230 emmccmd(u32int cmd, u32int arg, u32int *resp)
237 assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0);
238 c = (cmd << Indexshift) | cmdinfo[cmd];
241 if(r[Status] & Cmdinhibit){
242 print("emmccmd: need to reset Cmdinhibit intr %ux stat %ux\n",
243 r[Interrupt], r[Status]);
244 r[Control1] |= Srstcmd;
245 while(r[Control1] & Srstcmd)
247 while(r[Status] & Cmdinhibit)
250 if((c & Isdata || (c & Respmask) == Resp48busy) &&
251 r[Status] & Datinhibit){
252 print("emmccmd: need to reset Datinhibit intr %ux stat %ux\n",
253 r[Interrupt], r[Status]);
254 r[Control1] |= Srstdata;
255 while(r[Control1] & Srstdata)
257 while(r[Status] & Datinhibit)
261 if((i = r[Interrupt]) != 0){
263 print("emmc: before command, intr was %ux\n", i);
268 while(((i=r[Interrupt])&(Cmddone|Err)) == 0)
269 if((long)(m->ticks-now) > HZ)
271 if((i&(Cmddone|Err)) != Cmddone){
272 if((i&~Err) != Ctoerr)
273 print("emmc: cmd %ux error intr %ux stat %ux\n", c, i, r[Status]);
275 if(r[Status]&Cmdinhibit){
276 r[Control1] |= Srstcmd;
277 while(r[Control1]&Srstcmd)
282 r[Interrupt] = i & ~(Datadone|Readrdy|Writerdy);
283 switch(c & Respmask){
285 resp[0] = r[Resp0]<<8;
286 resp[1] = r[Resp0]>>24 | r[Resp1]<<8;
287 resp[2] = r[Resp1]>>24 | r[Resp2]<<8;
288 resp[3] = r[Resp2]>>24 | r[Resp3]<<8;
298 if((c & Respmask) == Resp48busy){
299 r[Irpten] = Datadone|Err;
300 tsleep(&emmc.r, datadone, 0, 3000);
304 if((i & Datadone) == 0)
305 print("emmcio: no Datadone after CMD%d\n", cmd);
307 print("emmcio: CMD%d error interrupt %ux\n",
312 * Once card is selected, use faster clock
314 if(cmd == MMCSelect){
316 r[Control1] = clkdiv(emmc.extclk / SDfreq - 1) |
317 DTO << Datatoshift | Clkgendiv | Clken | Clkintlen;
318 for(i = 0; i < 1000; i++){
320 if(r[Control1] & Clkstable)
327 * If card bus width changes, change host bus width
329 if(cmd == Setbuswidth)
332 r[Control0] &= ~Dwidth4;
335 r[Control0] |= Dwidth4;
342 emmciosetup(int, void *buf, int bsize, int bcount)
349 if(len > (0x1000<<7))
353 cleandse((uchar*)buf, (uchar*)buf+len);
354 clean2pa(pa, pa+len);
358 r[Blksizecnt] = 7<<12 | bcount<<16 | bsize;
359 r[Irpten] = Datadone|Err;
363 emmcio(int write, uchar *buf, int len)
368 tsleep(&emmc.r, datadone, 0, 3000);
374 if((i & Datadone) == 0){
375 print("emmcio: %d timeout intr %ux stat %ux\n",
376 write, i, r[Status]);
381 print("emmcio: %d error intr %ux stat %ux\n",
382 write, r[Interrupt], r[Status]);
390 uintptr pa = PADDR(buf);
391 invaldse((uchar*)buf, (uchar*)buf+len);
392 inval2pa(pa, pa+len);