From a8c50a79437006bdde702d09408df8749f48a45a Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Sun, 25 Aug 2019 18:45:29 +0200 Subject: [PATCH] bcm64: replace emmc2 driver with richard millers sdhc driver the new driver supports 50MHz highspeed bus mode and uses ADMA instead of SDMA. --- sys/src/9/bcm64/emmc2.c | 413 ----------------------------- sys/src/9/bcm64/pi4 | 2 +- sys/src/9/bcm64/sdhc.c | 565 ++++++++++++++++++++++++++++++++++++++++ sys/src/9/port/sdmmc.c | 35 +++ 4 files changed, 601 insertions(+), 414 deletions(-) delete mode 100644 sys/src/9/bcm64/emmc2.c create mode 100644 sys/src/9/bcm64/sdhc.c diff --git a/sys/src/9/bcm64/emmc2.c b/sys/src/9/bcm64/emmc2.c deleted file mode 100644 index 425fa5252..000000000 --- a/sys/src/9/bcm64/emmc2.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * external mass media controller (mmc / sd host interface) - * - * derived from Richard Miller's bcm/emmc.c - */ - -#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" - -enum { - Initfreq = 400000, /* initialisation frequency for MMC */ - SDfreq = 25000000, /* standard SD frequency */ - DTO = 14, /* data timeout exponent (guesswork) */ - - MMCSelect = 7, /* mmc/sd card select command */ - Setbuswidth = 6, /* mmc/sd set bus width command */ -}; - -enum { - /* Controller registers */ - Sysaddr = 0x00>>2, - Blksizecnt = 0x04>>2, - Arg1 = 0x08>>2, - Cmdtm = 0x0c>>2, - Resp0 = 0x10>>2, - Resp1 = 0x14>>2, - Resp2 = 0x18>>2, - Resp3 = 0x1c>>2, - Data = 0x20>>2, - Status = 0x24>>2, - Control0 = 0x28>>2, - Control1 = 0x2c>>2, - Interrupt = 0x30>>2, - Irptmask = 0x34>>2, - Irpten = 0x38>>2, - Control2 = 0x3c>>2, - Capabilities = 0x40>>2, - Forceirpt = 0x50>>2, - Boottimeout = 0x60>>2, - Dbgsel = 0x64>>2, - Spiintspt = 0xf0>>2, - Slotisrver = 0xfc>>2, - - /* Control0 */ - Dwidth4 = 1<<1, - Dwidth1 = 0<<1, - - /* Control1 */ - Srstdata = 1<<26, /* reset data circuit */ - Srstcmd = 1<<25, /* reset command circuit */ - Srsthc = 1<<24, /* reset complete host controller */ - Datatoshift = 16, /* data timeout unit exponent */ - Datatomask = 0xF0000, - Clkfreq8shift = 8, /* SD clock base divider LSBs */ - Clkfreq8mask = 0xFF00, - Clkfreqms2shift = 6, /* SD clock base divider MSBs */ - Clkfreqms2mask = 0xC0, - Clkgendiv = 0<<5, /* SD clock divided */ - Clkgenprog = 1<<5, /* SD clock programmable */ - Clken = 1<<2, /* SD clock enable */ - Pllen = 1<<3, - Clkstable = 1<<1, - Clkintlen = 1<<0, /* enable internal EMMC clocks */ - - /* Cmdtm */ - Indexshift = 24, - Suspend = 1<<22, - Resume = 2<<22, - Abort = 3<<22, - Isdata = 1<<21, - Ixchken = 1<<20, - Crcchken = 1<<19, - Respmask = 3<<16, - Respnone = 0<<16, - Resp136 = 1<<16, - Resp48 = 2<<16, - Resp48busy = 3<<16, - Multiblock = 1<<5, - Host2card = 0<<4, - Card2host = 1<<4, - Autocmd12 = 1<<2, - Autocmd23 = 2<<2, - Blkcnten = 1<<1, - Dmaen = 1<<0, - - /* Interrupt */ - Acmderr = 1<<24, - Denderr = 1<<22, - Dcrcerr = 1<<21, - Dtoerr = 1<<20, - Cbaderr = 1<<19, - Cenderr = 1<<18, - Ccrcerr = 1<<17, - Ctoerr = 1<<16, - Err = 1<<15, - Cardintr = 1<<8, - Cardinsert = 1<<6, - Readrdy = 1<<5, - Writerdy = 1<<4, - Dmaintr = 1<<3, - Datadone = 1<<1, - Cmddone = 1<<0, - - /* Status */ - Present = 1<<18, - Bufread = 1<<11, - Bufwrite = 1<<10, - Readtrans = 1<<9, - Writetrans = 1<<8, - Datactive = 1<<2, - Datinhibit = 1<<1, - Cmdinhibit = 1<<0, -}; - -static 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 | Card2host | Ixchken | Crcchken | Dmaen, -[18] Resp48 | Isdata | Card2host | Multiblock | Blkcnten | Ixchken | Crcchken | Dmaen, -[24] Resp48 | Isdata | Host2card | Ixchken | Crcchken | Dmaen, -[25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken | Dmaen, -[41] Resp48, -[55] Resp48 | Ixchken | Crcchken, -}; - -typedef struct Ctlr Ctlr; -struct Ctlr { - Rendez r; - u32int *regs; - int datadone; - int fastclock; - ulong extclk; - int irq; -}; - -static Ctlr emmc; - -static uint -clkdiv(uint d) -{ - uint v; - - assert(d < 1<<10); - v = (d << Clkfreq8shift) & Clkfreq8mask; - v |= ((d >> 8) << Clkfreqms2shift) & Clkfreqms2mask; - return v; -} - -static void -interrupt(Ureg*, void*) -{ - u32int *r; - u32int i; - - r = emmc.regs; - i = r[Interrupt]; - r[Interrupt] = i & (Datadone|Err); - emmc.datadone = i; - wakeup(&emmc.r); -} - -static int -datadone(void*) -{ - return emmc.datadone; -} - -static int -emmcinit(void) -{ - u32int *r; - int i; - - emmc.extclk = getclkrate(ClkEmmc2); - emmc.irq = IRQmmc; - r = (u32int*)(VIRTIO + 0x340000); - emmc.regs = r; - r[Control1] = Srsthc; - for(i = 0; i < 100; i++){ - delay(10); - if((r[Control1] & Srsthc) == 0) - return 0; - } - print("emmc: reset timeout!\n"); - return -1; -} - -static int -emmcinquiry(char *inquiry, int inqlen) -{ - uint ver; - - ver = emmc.regs[Slotisrver] >> 16; - return snprint(inquiry, inqlen, - "eMMC SD Host Controller %2.2x Version %2.2x", - ver&0xFF, ver>>8); -} - -static void -emmcenable(void) -{ - int i; - - emmc.regs[Control1] = clkdiv(emmc.extclk / Initfreq - 1) | DTO << Datatoshift | - Clkgendiv | Clken | Clkintlen; - for(i = 0; i < 1000; i++){ - delay(1); - if(emmc.regs[Control1] & Clkstable) - break; - } - if(i == 1000) - print("SD clock won't initialise!\n"); - - emmc.regs[Control1] |= Pllen; - for(i = 0; i < 1000; i++){ - delay(1); - if(emmc.regs[Control1] & Clkstable) - break; - } - if(i == 1000) - print("PLL clock won't initialise!\n"); - - emmc.regs[Control0] = (emmc.regs[Control0] & ~0xFF00) | 0xF00; // VDD1 bus power to 3.3V - emmc.regs[Irptmask] = ~(Dtoerr|Cardintr|Dmaintr); - intrenable(emmc.irq, interrupt, nil, BUSUNKNOWN, sdio.name); -} - -static int -emmccmd(u32int cmd, u32int arg, u32int *resp) -{ - ulong now; - u32int *r; - u32int c; - u32int i; - - assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0); - c = (cmd << Indexshift) | cmdinfo[cmd]; - - r = emmc.regs; - if(r[Status] & Cmdinhibit){ - print("emmccmd: need to reset Cmdinhibit intr %ux stat %ux\n", - r[Interrupt], r[Status]); - r[Control1] |= Srstcmd; - while(r[Control1] & Srstcmd) - ; - while(r[Status] & Cmdinhibit) - ; - } - if((c & Isdata || (c & Respmask) == Resp48busy) && - r[Status] & Datinhibit){ - print("emmccmd: need to reset Datinhibit intr %ux stat %ux\n", - r[Interrupt], r[Status]); - r[Control1] |= Srstdata; - while(r[Control1] & Srstdata) - ; - while(r[Status] & Datinhibit) - ; - } - r[Arg1] = arg; - if((i = r[Interrupt]) != 0){ - if(i != Cardinsert) - print("emmc: before command, intr was %ux\n", i); - r[Interrupt] = i; - } - coherence(); - r[Cmdtm] = c; - coherence(); - now = m->ticks; - while(((i=r[Interrupt])&(Cmddone|Err)) == 0) - if((long)(m->ticks-now) > HZ) - break; - if((i&(Cmddone|Err)) != Cmddone){ - if((i&~Err) != Ctoerr) - print("emmc: cmd %ux error intr %ux stat %ux\n", c, i, r[Status]); - r[Interrupt] = i; - if(r[Status]&Cmdinhibit){ - r[Control1] |= Srstcmd; - while(r[Control1]&Srstcmd) - ; - } - error(Eio); - } - r[Interrupt] = i & ~(Datadone|Readrdy|Writerdy); - switch(c & Respmask){ - case Resp136: - resp[0] = r[Resp0]<<8; - resp[1] = r[Resp0]>>24 | r[Resp1]<<8; - resp[2] = r[Resp1]>>24 | r[Resp2]<<8; - resp[3] = r[Resp2]>>24 | r[Resp3]<<8; - break; - case Resp48: - case Resp48busy: - resp[0] = r[Resp0]; - break; - case Respnone: - resp[0] = 0; - break; - } - if((c & Respmask) == Resp48busy){ - r[Irpten] = Datadone|Err; - tsleep(&emmc.r, datadone, 0, 3000); - i = emmc.datadone; - emmc.datadone = 0; - r[Irpten] = 0; - if((i & Datadone) == 0) - print("emmcio: no Datadone after CMD%d\n", cmd); - if(i & Err) - print("emmcio: CMD%d error interrupt %ux\n", - cmd, r[Interrupt]); - r[Interrupt] = i; - } - /* - * Once card is selected, use faster clock - */ - if(cmd == MMCSelect){ - delay(10); - r[Control1] = clkdiv(emmc.extclk / SDfreq - 1) | - DTO << Datatoshift | Clkgendiv | Clken | Clkintlen; - for(i = 0; i < 1000; i++){ - delay(1); - if(r[Control1] & Clkstable) - break; - } - delay(10); - emmc.fastclock = 1; - } - /* - * If card bus width changes, change host bus width - */ - if(cmd == Setbuswidth) - switch(arg){ - case 0: - r[Control0] &= ~Dwidth4; - break; - case 2: - r[Control0] |= Dwidth4; - break; - } - return 0; -} - -static void -emmciosetup(int, void *buf, int bsize, int bcount) -{ - u32int *r; - int len; - - len = bsize*bcount; - if(len > (0x1000<<7)) - error(Etoobig); - - dmaflush(1, buf, len); - - r = emmc.regs; - r[Sysaddr] = dmaaddr(buf); - r[Blksizecnt] = 7<<12 | bcount<<16 | bsize; - r[Irpten] = Datadone|Err; -} - -static void -emmcio(int write, uchar *buf, int len) -{ - u32int *r; - int i; - - tsleep(&emmc.r, datadone, 0, 3000); - i = emmc.datadone; - emmc.datadone = 0; - - r = emmc.regs; - r[Irpten] = 0; - if((i & Datadone) == 0){ - print("emmcio: %d timeout intr %ux stat %ux\n", - write, i, r[Status]); - r[Interrupt] = i; - error(Eio); - } - if(i & Err){ - print("emmcio: %d error intr %ux stat %ux\n", - write, r[Interrupt], r[Status]); - r[Interrupt] = i; - error(Eio); - } - if(i) - r[Interrupt] = i; - - if(!write) - dmaflush(0, buf, len); -} - -SDio sdio = { - "emmc2", - emmcinit, - emmcenable, - emmcinquiry, - emmccmd, - emmciosetup, - emmcio, -}; diff --git a/sys/src/9/bcm64/pi4 b/sys/src/9/bcm64/pi4 index e2394b785..766a2100c 100644 --- a/sys/src/9/bcm64/pi4 +++ b/sys/src/9/bcm64/pi4 @@ -45,7 +45,7 @@ ip misc uartmini uartpl011 - sdmmc emmc2 + sdmmc sdhc dma gic vcore diff --git a/sys/src/9/bcm64/sdhc.c b/sys/src/9/bcm64/sdhc.c new file mode 100644 index 000000000..f18f92ab9 --- /dev/null +++ b/sys/src/9/bcm64/sdhc.c @@ -0,0 +1,565 @@ +/* + * bcm2711 sd host controller + * + * Copyright © 2012,2019 Richard Miller + * + * adapted from emmc.c - the two should really be merged + */ + +#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 EMMCREGS (VIRTIO+0x340000) + +enum { + Extfreq = 100*Mhz, /* guess external clock frequency if */ + /* not available from vcore */ + Initfreq = 400000, /* initialisation frequency for MMC */ + SDfreq = 25*Mhz, /* standard SD frequency */ + SDfreqhs = 50*Mhz, /* high speed frequency */ + DTO = 14, /* data timeout exponent (guesswork) */ + + GoIdle = 0, /* mmc/sdio go idle state */ + MMCSelect = 7, /* mmc/sd card select command */ + Setbuswidth = 6, /* mmc/sd set bus width command */ + Switchfunc = 6, /* mmc/sd switch function command */ + Voltageswitch = 11, /* md/sdio switch to 1.8V */ + IORWdirect = 52, /* sdio read/write direct command */ + IORWextended = 53, /* sdio read/write extended command */ + Appcmd = 55, /* mmc/sd application command prefix */ +}; + +enum { + /* Controller registers */ + SDMAaddr = 0x00>>2, + Blksizecnt = 0x04>>2, + Arg1 = 0x08>>2, + Cmdtm = 0x0c>>2, + Resp0 = 0x10>>2, + Resp1 = 0x14>>2, + Resp2 = 0x18>>2, + Resp3 = 0x1c>>2, + Data = 0x20>>2, + Status = 0x24>>2, + Control0 = 0x28>>2, + Control1 = 0x2c>>2, + Interrupt = 0x30>>2, + Irptmask = 0x34>>2, + Irpten = 0x38>>2, + Control2 = 0x3c>>2, + Capability = 0x40>>2, + Forceirpt = 0x50>>2, + Dmadesc = 0x58>>2, + Boottimeout = 0x70>>2, + Dbgsel = 0x74>>2, + Exrdfifocfg = 0x80>>2, + Exrdfifoen = 0x84>>2, + Tunestep = 0x88>>2, + Tunestepsstd = 0x8c>>2, + Tunestepsddr = 0x90>>2, + Spiintspt = 0xf0>>2, + Slotisrver = 0xfc>>2, + + /* Control0 */ + Busvoltage = 7<<9, + V1_8 = 5<<9, + V3_0 = 6<<9, + V3_3 = 7<<9, + Buspower = 1<<8, + Dwidth8 = 1<<5, + Dmaselect = 3<<3, + DmaSDMA = 0<<3, + DmaADMA1 = 1<<3, + DmaADMA2 = 2<<3, + Hispeed = 1<<2, + Dwidth4 = 1<<1, + Dwidth1 = 0<<1, + LED = 1<<0, + + /* Control1 */ + Srstdata = 1<<26, /* reset data circuit */ + Srstcmd = 1<<25, /* reset command circuit */ + Srsthc = 1<<24, /* reset complete host controller */ + Datatoshift = 16, /* data timeout unit exponent */ + Datatomask = 0xF0000, + Clkfreq8shift = 8, /* SD clock base divider LSBs */ + Clkfreq8mask = 0xFF00, + Clkfreqms2shift = 6, /* SD clock base divider MSBs */ + Clkfreqms2mask = 0xC0, + Clkgendiv = 0<<5, /* SD clock divided */ + Clkgenprog = 1<<5, /* SD clock programmable */ + Clken = 1<<2, /* SD clock enable */ + Clkstable = 1<<1, + Clkintlen = 1<<0, /* enable internal EMMC clocks */ + + /* Cmdtm */ + Indexshift = 24, + Suspend = 1<<22, + Resume = 2<<22, + Abort = 3<<22, + Isdata = 1<<21, + Ixchken = 1<<20, + Crcchken = 1<<19, + Respmask = 3<<16, + Respnone = 0<<16, + Resp136 = 1<<16, + Resp48 = 2<<16, + Resp48busy = 3<<16, + Multiblock = 1<<5, + Host2card = 0<<4, + Card2host = 1<<4, + Autocmd12 = 1<<2, + Autocmd23 = 2<<2, + Blkcnten = 1<<1, + Dmaen = 1<<0, + + /* Interrupt */ + Admaerr = 1<<25, + Acmderr = 1<<24, + Denderr = 1<<22, + Dcrcerr = 1<<21, + Dtoerr = 1<<20, + Cbaderr = 1<<19, + Cenderr = 1<<18, + Ccrcerr = 1<<17, + Ctoerr = 1<<16, + Err = 1<<15, + Cardintr = 1<<8, + Cardinsert = 1<<6, /* not in Broadcom datasheet */ + Readrdy = 1<<5, + Writerdy = 1<<4, + Dmaintr = 1<<3, + Datadone = 1<<1, + Cmddone = 1<<0, + + /* Status */ + Bufread = 1<<11, /* not in Broadcom datasheet */ + Bufwrite = 1<<10, /* not in Broadcom datasheet */ + Readtrans = 1<<9, + Writetrans = 1<<8, + Datactive = 1<<2, + Datinhibit = 1<<1, + Cmdinhibit = 1<<0, +}; + +static int cmdinfo[64] = { +[0] Ixchken, +[2] Resp136, +[3] Resp48 | Ixchken | Crcchken, +[5] Resp48, +[6] Resp48 | Ixchken | Crcchken, +[7] Resp48busy | Ixchken | Crcchken, +[8] Resp48 | Ixchken | Crcchken, +[9] Resp136, +[11] Resp48 | Ixchken | Crcchken, +[12] Resp48busy | Ixchken | Crcchken, +[13] Resp48 | Ixchken | Crcchken, +[16] Resp48, +[17] Resp48 | Isdata | Card2host | Ixchken | Crcchken, +[18] Resp48 | Isdata | Card2host | Multiblock | Blkcnten | Ixchken | Crcchken, +[24] Resp48 | Isdata | Host2card | Ixchken | Crcchken, +[25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken, +[41] Resp48, +[52] Resp48 | Ixchken | Crcchken, +[53] Resp48 | Ixchken | Crcchken | Isdata, +[55] Resp48 | Ixchken | Crcchken, +}; + +typedef struct Adma Adma; +typedef struct Ctlr Ctlr; + +/* + * ADMA2 descriptor + * See SD Host Controller Simplified Specification Version 2.00 + */ + +struct Adma { + u32int desc; + u32int addr; +}; + +enum { + /* desc fields */ + Valid = 1<<0, + End = 1<<1, + Int = 1<<2, + Nop = 0<<4, + Tran = 2<<4, + Link = 3<<4, + OLength = 16, + /* maximum value for Length field */ + Maxdma = ((1<<16) - 4), +}; + +struct Ctlr { + Rendez r; + Rendez cardr; + int fastclock; + ulong extclk; + int appcmd; + Adma *dma; +}; + +static Ctlr emmc; + +static void mmcinterrupt(Ureg*, void*); + +static void +WR(int reg, u32int val) +{ + u32int *r = (u32int*)EMMCREGS; + + if(0)print("WR %2.2ux %ux\n", reg<<2, val); + coherence(); + r[reg] = val; +} + +static uint +clkdiv(uint d) +{ + uint v; + + assert(d < 1<<10); + v = (d << Clkfreq8shift) & Clkfreq8mask; + v |= ((d >> 8) << Clkfreqms2shift) & Clkfreqms2mask; + return v; +} + +static Adma* +dmaalloc(void *addr, int len) +{ + int n; + uintptr a; + Adma *adma, *p; + + a = (uintptr)addr; + n = (len + Maxdma-1) / Maxdma; + adma = sdmalloc(n * sizeof(Adma)); + for(p = adma; len > 0; p++){ + p->desc = Valid | Tran; + if(n == 1) + p->desc |= len<desc |= Maxdma<addr = dmaaddr((void*)a); + a += Maxdma; + len -= Maxdma; + n--; + } + cachedwbse(adma, (char*)p - (char*)adma); + return adma; +} + +static void +emmcclk(uint freq) +{ + u32int *r; + uint div; + int i; + + r = (u32int*)EMMCREGS; + div = emmc.extclk / (freq<<1); + if(emmc.extclk / (div<<1) > freq) + div++; + WR(Control1, clkdiv(div) | + DTO<> 16; + return snprint(inquiry, inqlen, + "BCM SD Host Controller %2.2x Version %2.2x", + ver&0xFF, ver>>8); +} + +static void +emmcenable(void) +{ + + WR(Control0, 0); + delay(1); + WR(Control0, V3_3 | Buspower | Dwidth1 | DmaADMA2); + WR(Control1, 0); + delay(1); + emmcclk(Initfreq); + WR(Irpten, 0); + WR(Irptmask, ~(Cardintr|Dmaintr)); + WR(Interrupt, ~0); + intrenable(IRQmmc, mmcinterrupt, nil, BUSUNKNOWN, "sdhc"); +} + +static int +emmccmd(u32int cmd, u32int arg, u32int *resp) +{ + u32int *r; + u32int c; + int i; + ulong now; + + r = (u32int*)EMMCREGS; + assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0); + c = (cmd << Indexshift) | cmdinfo[cmd]; + /* + * CMD6 may be Setbuswidth or Switchfunc depending on Appcmd prefix + */ + if(cmd == Switchfunc && !emmc.appcmd) + c |= Isdata|Card2host; + if(c & Isdata) + c |= Dmaen; + if(cmd == IORWextended){ + if(arg & (1<<31)) + c |= Host2card; + else + c |= Card2host; + if((r[Blksizecnt]&0xFFFF0000) != 0x10000) + c |= Multiblock | Blkcnten; + } + /* + * GoIdle indicates new card insertion: reset bus width & speed + */ + if(cmd == GoIdle){ + WR(Control0, r[Control0] & ~(Dwidth4|Hispeed)); + emmcclk(Initfreq); + } + if(r[Status] & Cmdinhibit){ + print("emmccmd: need to reset Cmdinhibit intr %ux stat %ux\n", + r[Interrupt], r[Status]); + WR(Control1, r[Control1] | Srstcmd); + while(r[Control1] & Srstcmd) + ; + while(r[Status] & Cmdinhibit) + ; + } + if((r[Status] & Datinhibit) && + ((c & Isdata) || (c & Respmask) == Resp48busy)){ + print("emmccmd: need to reset Datinhibit intr %ux stat %ux\n", + r[Interrupt], r[Status]); + WR(Control1, r[Control1] | Srstdata); + while(r[Control1] & Srstdata) + ; + while(r[Status] & Datinhibit) + ; + } + WR(Arg1, arg); + if((i = (r[Interrupt] & ~Cardintr)) != 0){ + if(i != Cardinsert) + print("emmc: before command, intr was %ux\n", i); + WR(Interrupt, i); + } + WR(Cmdtm, c); + now = m->ticks; + while(((i=r[Interrupt])&(Cmddone|Err)) == 0) + if(m->ticks-now > HZ) + break; + if((i&(Cmddone|Err)) != Cmddone){ + if((i&~(Err|Cardintr)) != Ctoerr) + print("emmc: cmd %ux arg %ux error intr %ux stat %ux\n", c, arg, i, r[Status]); + WR(Interrupt, i); + if(r[Status]&Cmdinhibit){ + WR(Control1, r[Control1]|Srstcmd); + while(r[Control1]&Srstcmd) + ; + } + error(Eio); + } + WR(Interrupt, i & ~(Datadone|Readrdy|Writerdy)); + switch(c & Respmask){ + case Resp136: + resp[0] = r[Resp0]<<8; + resp[1] = r[Resp0]>>24 | r[Resp1]<<8; + resp[2] = r[Resp1]>>24 | r[Resp2]<<8; + resp[3] = r[Resp2]>>24 | r[Resp3]<<8; + break; + case Resp48: + case Resp48busy: + resp[0] = r[Resp0]; + break; + case Respnone: + resp[0] = 0; + break; + } + if((c & Respmask) == Resp48busy){ + WR(Irpten, r[Irpten]|Datadone|Err); + tsleep(&emmc.r, datadone, 0, 3000); + i = r[Interrupt]; + if((i & Datadone) == 0) + print("emmcio: no Datadone after CMD%d\n", cmd); + if(i & Err) + print("emmcio: CMD%d error interrupt %ux\n", + cmd, r[Interrupt]); + WR(Interrupt, i); + } + /* + * Once card is selected, use faster clock + */ + if(cmd == MMCSelect){ + delay(1); + emmcclk(SDfreq); + delay(1); + emmc.fastclock = 1; + } + if(cmd == Setbuswidth){ + if(emmc.appcmd){ + /* + * If card bus width changes, change host bus width + */ + switch(arg){ + case 0: + WR(Control0, r[Control0] & ~Dwidth4); + break; + case 2: + WR(Control0, r[Control0] | Dwidth4); + break; + } + }else{ + /* + * If card switched into high speed mode, increase clock speed + */ + if((arg&0x8000000F) == 0x80000001){ + delay(1); + emmcclk(SDfreqhs); + delay(1); + } + } + }else if(cmd == IORWdirect && (arg & ~0xFF) == (1<<31|0<<28|7<<9)){ + switch(arg & 0x3){ + case 0: + WR(Control0, r[Control0] & ~Dwidth4); + break; + case 2: + WR(Control0, r[Control0] | Dwidth4); + //WR(Control0, r[Control0] | Hispeed); + break; + } + } + emmc.appcmd = (cmd == Appcmd); + return 0; +} + +static void +emmciosetup(int write, void *buf, int bsize, int bcount) +{ + int len; + + len = bsize * bcount; + assert(((uintptr)buf&3) == 0); + assert((len&3) == 0); + assert(bsize <= 2048); + WR(Blksizecnt, bcount<<16 | bsize); + if(emmc.dma) + sdfree(emmc.dma); + emmc.dma = dmaalloc(buf, len); + if(write) + cachedwbse(buf, len); + else + cachedwbinvse(buf, len); + WR(Dmadesc, dmaaddr(emmc.dma)); + okay(1); +} + +static void +emmcio(int write, uchar *buf, int len) +{ + u32int *r; + int i; + + r = (u32int*)EMMCREGS; + if(waserror()){ + okay(0); + nexterror(); + } + WR(Irpten, r[Irpten] | Datadone|Err); + tsleep(&emmc.r, datadone, 0, 3000); + WR(Irpten, r[Irpten] & ~(Datadone|Err)); + i = r[Interrupt]; + if((i & (Datadone|Err)) != Datadone){ + print("sdhc: %s error intr %ux stat %ux\n", + write? "write" : "read", i, r[Status]); + WR(Interrupt, i); + error(Eio); + } + WR(Interrupt, i); + if(!write) + cachedinvse(buf, len); + poperror(); + okay(0); +} + +static void +mmcinterrupt(Ureg*, void*) +{ + u32int *r; + int i; + + r = (u32int*)EMMCREGS; + i = r[Interrupt]; + if(i&(Datadone|Err)) + wakeup(&emmc.r); + if(i&Cardintr) + wakeup(&emmc.cardr); + WR(Irpten, r[Irpten] & ~i); +} + +SDio sdio = { + "sdhc", + emmcinit, + emmcenable, + emmcinquiry, + emmccmd, + emmciosetup, + emmcio, +}; diff --git a/sys/src/9/port/sdmmc.c b/sys/src/9/port/sdmmc.c index a153c7ffd..044982de6 100644 --- a/sys/src/9/port/sdmmc.c +++ b/sys/src/9/port/sdmmc.c @@ -28,6 +28,7 @@ enum { GO_IDLE_STATE = 0, ALL_SEND_CID = 2, SEND_RELATIVE_ADDR= 3, + SWITCH_FUNC = 6, SELECT_CARD = 7, SD_SEND_IF_COND = 8, SEND_CSD = 9, @@ -59,6 +60,13 @@ enum { Width1 = 0<<0, Width4 = 2<<0, + /* SWITCH_FUNC */ + Dfltspeed = 0<<0, + Hispeed = 1<<0, + Checkfunc = 0x00FFFFF0, + Setfunc = 0x80FFFFF0, + Funcbytes = 64, + /* OCR (operating conditions register) */ Powerup = 1<<31, }; @@ -167,6 +175,27 @@ mmcenable(SDev* dev) return 1; } +static void +mmcswitchfunc(SDio *io, int arg) +{ + uchar *buf; + int n; + u32int r[4]; + + n = Funcbytes; + buf = sdmalloc(n); + if(waserror()){ + print("mmcswitchfunc error\n"); + sdfree(buf); + nexterror(); + } + io->iosetup(0, buf, n, 1); + io->cmd(SWITCH_FUNC, arg, r); + io->io(0, buf, n); + sdfree(buf); + poperror(); +} + static int mmconline(SDunit *unit) { @@ -220,6 +249,12 @@ mmconline(SDunit *unit) io->cmd(SET_BLOCKLEN, unit->secsize, r); io->cmd(APP_CMD, ctl->rca<cmd(SET_BUS_WIDTH, Width4, r); + if(strcmp(io->name, "sdhc") == 0){ + if(!waserror()){ + mmcswitchfunc(io, Hispeed|Setfunc); + poperror(); + } + } poperror(); return 1; }