Storage driver for SD (and maybe MMC) cards. Reference: /n/sources/patch/applied/sdmmc-driver Date: Fri Oct 19 15:34:55 CES 2012 Signed-off-by: miller@hamnavoe.com --- /sys/src/9/port/sdmmc.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/port/sdmmc.c Fri Oct 19 15:34:00 2012 @@ -0,0 +1,320 @@ +#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" + +/* + * mmc / sd memory card + * + * + * Copyright © 2012 Richard Miller + * + * Assumes only one card on the bus + */ + +enum { + Inittimeout = 15, + Multiblock = 1, + +/* Commands */ + GO_IDLE_STATE = 0, + ALL_SEND_CID = 2, + SEND_RELATIVE_ADDR = 3, + SELECT_CARD = 7, + SD_SEND_IF_COND = 8, + SEND_CSD = 9, + STOP_TRANSMISSION = 12, + SEND_STATUS = 13, + SET_BLOCKLEN = 16, + READ_SINGLE_BLOCK = 17, + READ_MULTIPLE_BLOCK = 18, + WRITE_BLOCK = 24, + WRITE_MULTIPLE_BLOCK= 25, + APP_CMD = 55, /* prefix for following app-specific commands */ + SET_BUS_WIDTH = 6, + SD_SEND_OP_COND = 41, + +/* Command arguments */ + /* SD_SEND_IF_COND */ + Voltage = 1<<8, + Checkpattern = 0x42, + + /* SELECT_CARD */ + Rcashift = 16, + + /* SD_SEND_OP_COND */ + Hcs = 1<<30, /* host supports SDHC & SDXC */ + Ccs = 1<<30, /* card is SDHC or SDXC */ + V3_3 = 3<<20, /* 3.2-3.4 volts */ + + /* SET_BUS_WIDTH */ + Width1 = 0<<0, + Width4 = 2<<0, + +/* OCR (operating conditions register) */ + Powerup = 1<<31, +}; + +typedef struct Ctlr Ctlr; + +struct Ctlr { + SDev *dev; + SDio *io; + /* SD card registers */ + u16int rca; + u32int ocr; + u32int cid[4]; + u32int csd[4]; +}; + +extern SDifc sdmmcifc; +extern SDio sdio; + +static uint +rbits(u32int *p, uint start, uint len) +{ + uint w, off, v; + + w = start/32; + off = start%32; + if(off == 0) + v = p[w]; + else + v = p[w]>>off | p[w+1]<<(32-off); + if(len < 32) + return v & ((1<secsize = 1 << CSD(83, 80); + + switch(CSD(127, 126)){ + case 0: /* CSD version 1 */ + csize = CSD(73, 62); + mult = CSD(49, 47); + unit->sectors = (csize+1) * (1<<(mult+2)); + break; + case 1: /* CSD version 2 */ + csize = CSD(69, 48); + unit->sectors = (csize+1) * 512LL*KiB / unit->secsize; + break; + } + if(unit->secsize == 1024){ + unit->sectors <<= 1; + unit->secsize = 512; + } +} + +static SDev* +mmcpnp(void) +{ + SDev *sdev; + Ctlr *ctl; + + if(sdio.init() < 0) + return nil; + sdev = malloc(sizeof(SDev)); + if(sdev == nil) + return nil; + ctl = malloc(sizeof(Ctlr)); + if(ctl == nil){ + free(sdev); + return nil; + } + sdev->idno = 'M'; + sdev->ifc = &sdmmcifc; + sdev->nunit = 1; + sdev->ctlr = ctl; + ctl->dev = sdev; + ctl->io = &sdio; + return sdev; +} + +static int +mmcverify(SDunit *unit) +{ + Ctlr *ctl; + int n; + + ctl = unit->dev->ctlr; + n = ctl->io->inquiry((char*)&unit->inquiry[8], sizeof(unit->inquiry)-8); + if(n < 0) + return 0; + unit->inquiry[0] = SDperdisk; + unit->inquiry[1] = SDinq1removable; + unit->inquiry[4] = sizeof(unit->inquiry)-4; + return 1; +} + +static int +mmcenable(SDev* dev) +{ + Ctlr *ctl; + + ctl = dev->ctlr; + ctl->io->enable(); + return 1; +} + +static int +mmconline(SDunit *unit) +{ + Ctlr *ctl; + SDio *io; + u32int r[4]; + int hcs, i; + + ctl = unit->dev->ctlr; + io = ctl->io; + assert(unit->subno == 0); + + if(waserror()){ + unit->sectors = 0; + return 0; + } + if(unit->sectors != 0){ + io->cmd(SEND_STATUS, ctl->rca<cmd(GO_IDLE_STATE, 0, r); + hcs = 0; + if(!waserror()){ + io->cmd(SD_SEND_IF_COND, Voltage|Checkpattern, r); + if(r[0] == (Voltage|Checkpattern)){ + /* SD 2.0 or above */ + hcs = Hcs; + } + poperror(); + } + for(i = 0; i < Inittimeout; i++){ + delay(100); + io->cmd(APP_CMD, 0, r); + io->cmd(SD_SEND_OP_COND, hcs|V3_3, r); + if(r[0]&Powerup) + break; + } + if(i == Inittimeout){ + print("sdmmc: card won't power up\n"); + poperror(); + return 2; + } + ctl->ocr = r[0]; + io->cmd(ALL_SEND_CID, 0, r); + memmove(ctl->cid, r, sizeof(ctl->cid)); + io->cmd(SEND_RELATIVE_ADDR, 0, r); + ctl->rca = r[0]>>16; + io->cmd(SEND_CSD, ctl->rca<csd, r, sizeof(ctl->csd)); + identify(unit, ctl->csd); + io->cmd(SELECT_CARD, ctl->rca<cmd(SET_BLOCKLEN, unit->secsize, r); + io->cmd(APP_CMD, ctl->rca<cmd(SET_BUS_WIDTH, Width4, r); + poperror(); + return 1; +} + +static int +mmcrctl(SDunit *unit, char *p, int l) +{ + Ctlr *ctl; + int i, n; + + ctl = unit->dev->ctlr; + assert(unit->subno == 0); + if(unit->sectors == 0){ + mmconline(unit); + if(unit->sectors == 0) + return 0; + } + n = snprint(p, l, "rca %4.4ux ocr %8.8ux\ncid ", ctl->rca, ctl->ocr); + for(i = nelem(ctl->cid)-1; i >= 0; i--) + n += snprint(p+n, l-n, "%8.8ux", ctl->cid[i]); + n += snprint(p+n, l-n, " csd "); + for(i = nelem(ctl->csd)-1; i >= 0; i--) + n += snprint(p+n, l-n, "%8.8ux", ctl->csd[i]); + n += snprint(p+n, l-n, "\ngeometry %llud %ld\n", + unit->sectors, unit->secsize); + return n; +} + +static long +mmcbio(SDunit *unit, int lun, int write, void *data, long nb, uvlong bno) +{ + Ctlr *ctl; + SDio *io; + uchar *buf; + u32int r[4]; + ulong b; + int len, tries; + + USED(lun); + ctl = unit->dev->ctlr; + io = ctl->io; + assert(unit->subno == 0); + if(unit->sectors == 0) + error("media change"); + buf = data; + len = unit->secsize; + if(Multiblock){ + b = bno; + tries = 0; + while(waserror()) + if(++tries == 3) + nexterror(); + io->iosetup(write, buf, len, nb); + if(waserror()){ + io->cmd(STOP_TRANSMISSION, 0, r); + nexterror(); + } + io->cmd(write? WRITE_MULTIPLE_BLOCK : READ_MULTIPLE_BLOCK, + ctl->ocr&Ccs? b : b*len, + r); + io->io(write, buf, nb*len); + poperror(); + io->cmd(STOP_TRANSMISSION, 0, r); + poperror(); + b += nb; + }else{ + for(b = bno; b < bno+nb; b++){ + io->iosetup(write, buf, len, 1); + io->cmd(write? WRITE_BLOCK : READ_SINGLE_BLOCK, + ctl->ocr&Ccs? b : b*len, + r); + io->io(write, buf, len); + buf += len; + } + } + return (b-bno)*len; +} + +static int +mmcrio(SDreq*) +{ + return -1; +} + +SDifc sdmmcifc = { + .name = "mmc", + .pnp = mmcpnp, + .enable = mmcenable, + .verify = mmcverify, + .online = mmconline, + .rctl = mmcrctl, + .bio = mmcbio, + .rio = mmcrio, +}; --- /sys/src/9/port/sd.h Fri Oct 19 15:34:03 2012 +++ /sys/src/9/port/sd.h Sat Nov 10 12:48:57 2012 @@ -7,6 +7,7 @@ typedef struct SDperm SDperm; typedef struct SDreq SDreq; typedef struct SDunit SDunit; +typedef struct SDio SDio; struct SDperm { char* name; @@ -144,6 +145,22 @@ #define sdmalloc(n) malloc(n) #define sdfree(p) free(p) #endif + +/* + * mmc/sd/sdio host controller interface + */ + +struct SDio { + char *name; + int (*init)(void); + void (*enable)(void); + int (*inquiry)(char*, int); + int (*cmd)(u32int, u32int, u32int*); + void (*iosetup)(int, void*, int, int); + void (*io)(int, uchar*, int); +}; + +extern SDio sdio; /* devsd.c */ extern void sdadddevs(SDev*); --- /sys/src/9/kw/sdio.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/kw/sdio.c Sat Nov 10 12:49:38 2012 @@ -0,0 +1,527 @@ +#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" + +/* + * kirkwood SDIO / SDMem / MMC host interface + */ + +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 = 0x00>>2, + DmaMSB = 0x04>>2, + Blksize = 0x08>>2, + Blkcount = 0x0c>>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, +}; + +#define TM(bits) ((bits)<<16) +#define GETTM(bits) (((bits)>>16)&0xFFFF) +#define GETCMD(bits) ((bits)&0xFFFF) + +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, +}; + +typedef struct Ctlr Ctlr; + +struct Ctlr { + Rendez r; + int datadone; + int fastclock; +}; + +static Ctlr ctlr; + +static void sdiointerrupt(Ureg*, void*); + +void +WR(int reg, u32int val) +{ + u32int *r = (u32int*)AddrSdio; + + val &= 0xFFFF; + if(0)print("WR %2.2ux %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 +sdiocmd(u32int cmd, u32int arg, u32int *resp) +{ + u32int *r; + u32int c; + int i, err; + ulong now; + + r = (u32int*)AddrSdio; + assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0); + i = GETTM(cmdinfo[cmd]); + c = (cmd<>16); + WR(ErrIntr, ~0); + WR(Cmd, c); + now = m->ticks; + while(((i=r[Interrupt])&(Cmddone|Err)) == 0) + if(m->ticks-now > HZ) + break; + if((i&(Cmddone|Err)) != Cmddone){ + if((err = r[ErrIntr]) != Ctoerr) + print("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); + switch(c&Respmask){ + 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; + } + if((c&Respmask) == Resp48busy){ + if(Polldone){ + now = m->ticks; + while(((i=r[Interrupt])&(Datadone|Err)) == 0) + if(m->ticks-now > 3*HZ) + break; + }else{ + WR(Irpten, Datadone|Err); + tsleep(&ctlr.r, datadone, 0, 3000); + i = ctlr.datadone; + ctlr.datadone = 0; + WR(Irpten, 0); + } + if((i&Datadone) == 0) + print("sdioio: no Datadone after CMD%d\n", cmd); + if(i&Err) + print("sdioio: CMD%d error interrupt %ux %ux\n", cmd, r[Interrupt], r[ErrIntr]); + WR(Interrupt, i); + } + /* + * Once card is selected, use faster clock + */ + if(cmd == MMCSelect){ + delay(10); + WR(Clockdiv, clkdiv(Clkfreq/SDfreq-1)); + delay(10); + ctlr.fastclock = 1; + } + /* + * If card bus width changes, change host bus width + */ + if(cmd == Setbuswidth){ + switch(arg){ + case 0: + WR(Hostctl, r[Hostctl]&~Dwidth4); + break; + case 2: + WR(Hostctl, r[Hostctl]|Dwidth4); + break; + } + } + return 0; +} + +static void +sdioiosetup(int write, void *buf, int bsize, int bcount) +{ + uintptr pa; + int len; + + 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 void +sdioio(int write, uchar *buf, int len) +{ + u32int *r; + int i, err, d, now; + + r = (u32int*)AddrSdio; + assert((len&3) == 0); + if(write && PIOwrite){ + while(len > 0){ + if(Pollwrite){ + now = m->ticks; + while(((i = r[Interrupt])&(Writerdy|Err)) == 0) + if(m->ticks-now > 8*HZ){ + print("sdioio: (%d) no Writerdy intr %ux stat %ux\n", len, i, r[Hoststat]); + error(Eio); + } + }else{ + if(((i = r[Interrupt])&(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; + continue; + } + if(i&Err){ + err = r[ErrIntr]; + print("sdioio: (%d) write error intr %ux err %ux stat %ux\n", len, i, err, r[Hoststat]); + WR(ErrIntr, err); + WR(Interrupt, i); + error(Eio); + } + } + }else if(!write && PIOread){ + while(len > 0){ + if(Pollread){ + now = m->ticks; + while(((i = r[Interrupt])&(Read8wready|Readrdy|Err)) == 0) + if(m->ticks-now > 3*HZ){ + print("sdioio: (%d) no Readrdy intr %ux stat %ux\n", len, i, r[Hoststat]); + error(Eio); + } + }else{ + if(((i = r[Interrupt])&(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*4){ + for(i = 0; i < 8*2; i++){ + d = r[Data]; + buf[0] = d; + buf[1] = d>>8; + buf += 2; + } + len -= 8*4; + continue; + } + if(i&Readrdy){ + d = r[Data]; + buf[0] = d; + buf[1] = d>>8; + d = r[Data]; + buf[2] = d; + buf[3] = d>>8; + buf += 4; + len -= 4; + continue; + } + if(i&Err){ + err = r[ErrIntr]; + print("sdioio: (%d) read error intr %ux err %ux stat %ux\n", len, i, err, r[Hoststat]); + WR(ErrIntr, err); + WR(Interrupt, i); + error(Eio); + } + } + }else{ + WR(Irpten, Dmadone|Err); + tsleep(&ctlr.r, datadone, 0, 3000); + WR(Irpten, 0); + i = ctlr.datadone; + ctlr.datadone = 0; + if(i&Err){ + err = r[ErrIntr]; + print("sdioio: (%d) dma error intr %ux err %ux stat %ux\n", len, i, err, r[Hoststat]); + WR(ErrIntr, err); + WR(Interrupt, i); + error(Eio); + } + if((i&Dmadone) == 0){ + print("sdioio: no dma end intr %ux stat %ux\n", i, r[Hoststat]); + WR(Interrupt, i); + error(Eio); + } + WR(Interrupt, Dmadone); + } + if(Polldone){ + now = m->ticks; + while(((i = r[Interrupt])&(Datadone|Err)) == 0) + if(m->ticks-now > 3*HZ) + break; + }else if((i&Datadone) == 0){ + WR(Irpten, Datadone|Err); + tsleep(&ctlr.r, datadone, 0, 3000); + i = ctlr.datadone; + ctlr.datadone = 0; + WR(Irpten, 0); + } + if(i&Err){ + err = r[ErrIntr]; + print("sdioio: %d error intr %ux %ux stat %ux\n", + write, i, err, r[Hoststat]); + WR(ErrIntr, err); + WR(Interrupt, i); + error(Eio); + } + if((i&Datadone) == 0){ + print("sdioio: %d timeout intr %ux stat %ux\n", + write, i, r[Hoststat]); + WR(Interrupt, i); + error(Eio); + } + 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, +}; --- /sys/src/9/kw/plug Fri Oct 19 15:34:09 2012 +++ /sys/src/9/kw/plug Fri Oct 19 15:34:07 2012 @@ -57,6 +57,7 @@ rdb coproc sdaoe sdscsi + sdmmc sdio softfpu syscall uartkw --- /sys/src/9/kw/fns.h Sat Nov 10 12:50:20 2012 +++ /sys/src/9/kw/fns.h Sat Nov 10 12:50:36 2012 @@ -113,6 +113,8 @@ uintptr mmukmap(uintptr, uintptr, usize); uintptr mmukunmap(uintptr, uintptr, usize); extern void* mmuuncache(void*, usize); +#define sdfree(p) free(p) +#define sdmalloc(n) mallocalign(n, CACHELINESZ, 0, 0) extern void* ucalloc(usize); extern void* ucallocalign(usize size, int align, int span); extern Block* ucallocb(int);