123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- /*
- * kirkwood SDIO / SDMem / MMC host interface
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "../port/error.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #include "../port/sd.h"
- #define TM(bits) ((bits)<<16)
- #define GETTM(bits) (((bits)>>16)&0xFFFF)
- #define GETCMD(bits) ((bits)&0xFFFF)
- typedef struct Ctlr Ctlr;
- enum {
- Clkfreq = 100000000, /* external clk frequency */
- Initfreq= 400000, /* initialisation frequency for MMC */
- SDfreq = 25000000, /* standard SD frequency */
- PIOread = 0, /* use programmed i/o (not dma) for reading */
- PIOwrite= 0, /* use programmed i/o (not dma) writing */
- Polldone= 0, /* poll for Datadone status, don't use interrupt */
- Pollread= 1, /* poll for reading blocks */
- Pollwrite= 1, /* poll for writing blocks */
- MMCSelect= 7, /* mmc/sd card select command */
- Setbuswidth= 6, /* mmc/sd set bus width command */
- };
- enum {
- /* Controller registers */
- DmaLSB = 0x0>>2,
- DmaMSB = 0x4>>2,
- Blksize = 0x8>>2,
- Blkcount = 0xc>>2,
- ArgLSB = 0x10>>2,
- ArgMSB = 0x14>>2,
- Tm = 0x18>>2,
- Cmd = 0x1c>>2,
- Resp0 = 0x20>>2,
- Resp1 = 0x24>>2,
- Resp2 = 0x28>>2,
- Resp3 = 0x2c>>2,
- Resp4 = 0x30>>2,
- Resp5 = 0x34>>2,
- Resp6 = 0x38>>2,
- Resp7 = 0x3c>>2,
- Data = 0x40>>2,
- Hoststat = 0x48>>2,
- Hostctl = 0x50>>2,
- Clockctl = 0x58>>2,
- Softreset = 0x5C>>2,
- Interrupt = 0x60>>2,
- ErrIntr = 0x64>>2,
- Irptmask = 0x68>>2,
- ErrIrptmask = 0x6C>>2,
- Irpten = 0x70>>2,
- ErrIrpten = 0x74>>2,
- Mbuslo = 0x100>>2,
- Mbushi = 0x104>>2,
- Win0ctl = 0x108>>2,
- Win0base = 0x10c>>2,
- Win1ctl = 0x110>>2,
- Win1base = 0x114>>2,
- Win2ctl = 0x118>>2,
- Win2base = 0x11c>>2,
- Win3ctl = 0x120>>2,
- Win3base = 0x124>>2,
- Clockdiv = 0x128>>2,
- /* Hostctl */
- Timeouten = 1<<15,
- Datatoshift = 11,
- Datatomask = 0x7800,
- Hispeed = 1<<10,
- Dwidth4 = 1<<9,
- Dwidth1 = 0<<9,
- Bigendian = 1<<3,
- LSBfirst = 1<<4,
- Cardtypemask = 3<<1,
- Cardtypemem = 0<<1,
- Cardtypeio = 1<<1,
- Cardtypeiomem = 2<<1,
- Cardtypsdio = 3<<1,
- Pushpullen = 1<<0,
- /* Clockctl */
- Sdclken = 1<<0,
- /* Softreset */
- Swreset = 1<<8,
- /* Cmd */
- Indexshift = 8,
- Isdata = 1<<5,
- Ixchken = 1<<4,
- Crcchken = 3<<2,
- Respmask = 3<<0,
- Respnone = 0<<0,
- Resp136 = 1<<0,
- Resp48 = 2<<0,
- Resp48busy = 3<<0,
- /* Tm */
- Hostdma = 0<<6,
- Hostpio = 1<<6,
- Stopclken = 1<<5,
- Host2card = 0<<4,
- Card2host = 1<<4,
- Autocmd12 = 1<<2,
- Hwwrdata = 1<<1,
- Swwrdata = 1<<0,
- /* ErrIntr */
- Crcstaterr = 1<<14,
- Crcstartbiterr = 1<<13,
- Crcendbiterr = 1<<12,
- Resptbiterr = 1<<11,
- Xfersizeerr = 1<<10,
- Cmdstarterr = 1<<9,
- Acmderr = 1<<8,
- Denderr = 1<<6,
- Dcrcerr = 1<<5,
- Dtoerr = 1<<4,
- Cbaderr = 1<<3,
- Cenderr = 1<<2,
- Ccrcerr = 1<<1,
- Ctoerr = 1<<0,
- /* Interrupt */
- Err = 1<<15,
- Write8ready = 1<<11,
- Read8wready = 1<<10,
- Cardintr = 1<<8,
- Readrdy = 1<<5,
- Writerdy = 1<<4,
- Dmadone = 1<<3,
- Blockgap = 1<<2,
- Datadone = 1<<1,
- Cmddone = 1<<0,
- /* Hoststat */
- Fifoempty = 1<<13,
- Fifofull = 1<<12,
- Rxactive = 1<<9,
- Txactive = 1<<8,
- Cardbusy = 1<<1,
- Cmdinhibit = 1<<0,
- };
- int cmdinfo[64] = {
- [0] Ixchken,
- [2] Resp136,
- [3] Resp48 | Ixchken | Crcchken,
- [6] Resp48 | Ixchken | Crcchken,
- [7] Resp48busy | Ixchken | Crcchken,
- [8] Resp48 | Ixchken | Crcchken,
- [9] Resp136,
- [12] Resp48busy | Ixchken | Crcchken,
- [13] Resp48 | Ixchken | Crcchken,
- [16] Resp48,
- [17] Resp48 | Isdata | TM(Card2host) | Ixchken | Crcchken,
- [18] Resp48 | Isdata | TM(Card2host) | Ixchken | Crcchken,
- [24] Resp48 | Isdata | TM(Host2card | Hwwrdata) | Ixchken | Crcchken,
- [25] Resp48 | Isdata | TM(Host2card | Hwwrdata) | Ixchken | Crcchken,
- [41] Resp48,
- [55] Resp48 | Ixchken | Crcchken,
- };
- struct Ctlr {
- Rendez r;
- int datadone;
- int fastclock;
- };
- static Ctlr ctlr;
- static void sdiointerrupt(Ureg*, void*);
- void
- WR(int reg, u32int val)
- {
- u32int *r;
- r = (u32int*)AddrSdio;
- val &= 0xFFFF;
- if(0)iprint("WR %#4.4ux %#ux\n", reg<<2, val);
- r[reg] = val;
- }
- static uint
- clkdiv(uint d)
- {
- assert(d < 1<<11);
- return d;
- }
- static int
- datadone(void*)
- {
- return ctlr.datadone;
- }
- static int
- sdioinit(void)
- {
- u32int *r;
- r = (u32int*)AddrSdio;
- WR(Softreset, Swreset);
- while(r[Softreset] & Swreset)
- ;
- delay(10);
- return 0;
- }
- static int
- sdioinquiry(char *inquiry, int inqlen)
- {
- return snprint(inquiry, inqlen, "SDIO Host Controller");
- }
- static void
- sdioenable(void)
- {
- u32int *r;
- r = (u32int*)AddrSdio;
- WR(Clockdiv, clkdiv(Clkfreq/Initfreq - 1));
- delay(10);
- WR(Clockctl, r[Clockctl] & ~Sdclken);
- WR(Hostctl, Pushpullen|Bigendian|Cardtypemem);
- WR(Irpten, 0);
- WR(Interrupt, ~0);
- WR(ErrIntr, ~0);
- WR(Irptmask, ~0);
- WR(ErrIrptmask, ~Dtoerr);
- intrenable(Irqlo, IRQ0sdio, sdiointerrupt, &ctlr, "sdio");
- }
- static int
- awaitdone(u32int *r, int bits, int ticks)
- {
- int i;
- ulong start;
- start = m->ticks;
- while(((i = r[Interrupt]) & (bits|Err)) == 0)
- if(m->ticks - start > ticks)
- break;
- return i;
- }
- static void
- ckerr(u32int *r, int i, int len, char *op)
- {
- int err;
- if(i & Err){
- err = r[ErrIntr];
- iprint("sdioio: (%d) %s error intr %#ux err %#ux stat %#ux\n",
- len, op, i, err, r[Hoststat]);
- WR(ErrIntr, err);
- WR(Interrupt, i);
- error(Eio);
- }
- }
- static void
- ckdmadone(u32int *r, int i, char *msg)
- {
- if((i & Dmadone) == 0){
- iprint("sdioio: %s intr %#ux stat %#ux\n", msg, i, r[Hoststat]);
- WR(Interrupt, i);
- error(Eio);
- }
- }
- static void
- getresp(u32int *r, u32int *resp, int resptype)
- {
- switch(resptype){
- case Resp136:
- resp[0] = r[Resp7]<<8 | r[Resp6]<<22;
- resp[1] = r[Resp6]>>10 | r[Resp5]<<6 | r[Resp4]<<22;
- resp[2] = r[Resp4]>>10 | r[Resp3]<<6 | r[Resp2]<<22;
- resp[3] = r[Resp2]>>10 | r[Resp1]<<6 | r[Resp0]<<22;
- break;
- case Resp48:
- case Resp48busy:
- resp[0] = r[Resp2] | r[Resp1]<<6 | r[Resp0]<<22;
- break;
- case Respnone:
- resp[0] = 0;
- break;
- }
- }
- static void
- awaitresp48data(u32int *r, u32int cmd)
- {
- int i;
- if(Polldone)
- i = awaitdone(r, Datadone, 3*HZ);
- else{
- WR(Irpten, Datadone|Err);
- tsleep(&ctlr.r, datadone, 0, 3000);
- i = ctlr.datadone;
- ctlr.datadone = 0;
- WR(Irpten, 0);
- }
- if((i & Datadone) == 0)
- iprint("sdioio: no Datadone after CMD%d\n", cmd);
- if(i & Err)
- iprint("sdioio: CMD%d error interrupt %#ux %#ux\n",
- cmd, r[Interrupt], r[ErrIntr]);
- WR(Interrupt, i);
- }
- static void
- finishcmd(u32int cmd, u32int arg)
- {
- u32int *r;
- /*
- * Once card is selected, use faster clock.
- * If card bus width changes, change host bus width.
- */
- r = (u32int*)AddrSdio;
- if(cmd == MMCSelect){
- delay(10);
- WR(Clockdiv, clkdiv(Clkfreq/SDfreq - 1));
- delay(10);
- ctlr.fastclock = 1;
- } else if(cmd == Setbuswidth)
- switch(arg){
- case 0:
- WR(Hostctl, r[Hostctl] & ~Dwidth4);
- break;
- case 2:
- WR(Hostctl, r[Hostctl] | Dwidth4);
- break;
- }
- }
- static int
- sdiocmd(u32int cmd, u32int arg, u32int *resp)
- {
- int i, err;
- u32int c;
- u32int *r;
- assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0);
- i = GETTM(cmdinfo[cmd]);
- c = cmd<<Indexshift | GETCMD(cmdinfo[cmd]);
- if(c & Isdata)
- if(i & Card2host)
- i |= PIOread? Hostpio: Hostdma;
- else
- i |= PIOwrite? Hostpio: Hostdma;
- WR(Tm, i);
- WR(ArgLSB, arg);
- WR(ArgMSB, arg>>16);
- WR(ErrIntr, ~0);
- WR(Cmd, c);
- r = (u32int*)AddrSdio;
- i = awaitdone(r, Cmddone, HZ);
- if((i & (Cmddone|Err)) != Cmddone){
- if((err = r[ErrIntr]) != Ctoerr)
- iprint("sdio: cmd %#ux error intr %#ux %#ux stat %#ux\n",
- c, i, err, r[Hoststat]);
- WR(ErrIntr, err);
- WR(Interrupt, i);
- error(Eio);
- }
- WR(Interrupt, i & ~Datadone);
- c &= Respmask;
- getresp(r, resp, c);
- if(c == Resp48busy)
- awaitresp48data(r, cmd);
- finishcmd(cmd, arg);
- return 0;
- }
- static void
- sdioiosetup(int write, void *buf, int bsize, int bcount)
- {
- int len;
- uintptr pa;
- pa = PADDR(buf);
- if(write && !PIOwrite){
- WR(DmaLSB, pa);
- WR(DmaMSB, pa>>16);
- len = bsize * bcount;
- cachedwbse(buf, len);
- l2cacheuwbse(buf, len);
- }else if(!write && !PIOread){
- WR(DmaLSB, pa);
- WR(DmaMSB, pa>>16);
- len = bsize * bcount;
- cachedwbinvse(buf, len);
- l2cacheuwbinvse(buf, len);
- }
- WR(Blksize, bsize);
- WR(Blkcount, bcount);
- }
- static uchar *
- getdatas(u32int *r, uchar *buf)
- {
- ushort d;
- d = r[Data];
- *buf++ = d;
- *buf++ = d>>8;
- return buf;
- }
- static int
- sdioread(uchar *buf, int *lenp)
- {
- int i, now, len;
- u32int *r;
- r = (u32int*)AddrSdio;
- i = 0;
- len = *lenp;
- while(len > 0){
- if(Pollread){
- now = m->ticks;
- i = awaitdone(r, Read8wready|Readrdy, 3*HZ);
- if(m->ticks - now > 3*HZ){
- print("sdioio: (%d) no Readrdy intr %#ux stat %#ux\n",
- len, i, r[Hoststat]);
- error(Eio);
- }
- }else{
- i = r[Interrupt];
- if((i & (Read8wready|Readrdy|Err)) == 0){
- WR(Irpten, (len > 8*4? Read8wready:
- Readrdy) | Err);
- tsleep(&ctlr.r, datadone, 0, 3000);
- WR(Irpten, 0);
- i = ctlr.datadone;
- ctlr.datadone = 0;
- if((i & (Read8wready|Readrdy|Err)) == 0){
- print("sdioio: (%d) no Readrdy intr %#ux stat %#ux\n",
- len, i, r[Hoststat]);
- error(Eio);
- }
- }
- }
- if((i & Read8wready) && len >= 8*2*2){
- for(i = 0; i < 8*2; i++)
- buf = getdatas(r, buf);
- len -= 8*2*2;
- }else if(i & Readrdy){
- buf = getdatas(r, buf);
- buf = getdatas(r, buf);
- len -= 2*2;
- } else
- ckerr(r, i, len, "read");
- }
- *lenp = len;
- return i;
- }
- static int
- sdiowrite(uchar *buf, int *lenp)
- {
- int i, now, len;
- u32int *r;
- r = (u32int*)AddrSdio;
- i = 0;
- len = *lenp;
- while(len > 0){
- if(Pollwrite){
- now = m->ticks;
- i = awaitdone(r, Writerdy, 8*HZ);
- if(m->ticks - now > 8*HZ){
- print("sdioio: (%d) no Writerdy intr %#ux stat %#ux\n",
- len, i, r[Hoststat]);
- error(Eio);
- }
- }else{
- i = r[Interrupt];
- if((i & (Writerdy|Err)) == 0){
- WR(Irpten, Writerdy | Err);
- tsleep(&ctlr.r, datadone, 0, 8000);
- WR(Irpten, 0);
- i = ctlr.datadone;
- ctlr.datadone = 0;
- if((i & (Writerdy|Err)) == 0){
- print("sdioio: (%d) no Writerdy intr %#ux stat %#ux\n",
- len, i, r[Hoststat]);
- error(Eio);
- }
- }
- }
- if(i & Writerdy){
- r[Data] = buf[0] | buf[1]<<8;
- r[Data] = buf[2] | buf[3]<<8;
- buf += 4;
- len -= 4;
- } else
- ckerr(r, i, len, "write");
- }
- *lenp = len;
- return i;
- }
- static void
- sdioio(int write, uchar *buf, int len)
- {
- int i;
- u32int *r;
- assert((len & 3) == 0);
- r = (u32int*)AddrSdio;
- if(write && PIOwrite)
- i = sdiowrite(buf, &len);
- else if(!write && PIOread)
- i = sdioread(buf, &len);
- else{
- WR(Irpten, Dmadone|Err);
- tsleep(&ctlr.r, datadone, 0, 3000);
- WR(Irpten, 0);
- i = ctlr.datadone;
- ctlr.datadone = 0;
- ckerr(r, i, len, "dma");
- ckdmadone(r, i, "no dma done");
- WR(Interrupt, Dmadone);
- }
- if(Polldone)
- i = awaitdone(r, Datadone, 3*HZ);
- else if((i & Datadone) == 0){
- WR(Irpten, Datadone|Err);
- tsleep(&ctlr.r, datadone, 0, 3000);
- i = ctlr.datadone;
- ctlr.datadone = 0;
- WR(Irpten, 0);
- }
- ckerr(r, i, len, "IO");
- ckdmadone(r, i, "IO timeout");
- if(i)
- WR(Interrupt, i);
- }
- static void
- sdiointerrupt(Ureg*, void*)
- {
- u32int *r;
- r = (u32int*)AddrSdio;
- ctlr.datadone = r[Interrupt];
- WR(Irpten, 0);
- wakeup(&ctlr.r);
- }
- SDio sdio = {
- "sdio",
- sdioinit,
- sdioenable,
- sdioinquiry,
- sdiocmd,
- sdioiosetup,
- sdioio,
- };
|