2013-07-13 23:33:07 +00:00
|
|
|
/*
|
|
|
|
* pci mmc controller.
|
|
|
|
*
|
|
|
|
* initially written for X230 Ricoh MMC controller.
|
|
|
|
* cmdinfo[] table stolen from bcm/emmc.c, thanks richard.
|
|
|
|
*
|
|
|
|
* for sdhc documentation see: https://www.sdcard.org/
|
|
|
|
*/
|
|
|
|
#include "u.h"
|
|
|
|
#include "../port/lib.h"
|
|
|
|
#include "../port/error.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
#include "io.h"
|
2020-09-13 18:33:17 +00:00
|
|
|
#include "../port/pci.h"
|
2013-07-13 23:33:07 +00:00
|
|
|
#include "../port/sd.h"
|
|
|
|
|
|
|
|
/* registers */
|
|
|
|
enum {
|
|
|
|
Rsdma = 0x00,
|
|
|
|
Rbsize = 0x04,
|
|
|
|
Rbcount = 0x06,
|
|
|
|
Rarg = 0x08,
|
|
|
|
Rmode = 0x0C,
|
|
|
|
Rcmd = 0x0E,
|
|
|
|
Rresp0 = 0x10,
|
|
|
|
Rresp1 = 0x14,
|
|
|
|
Rresp2 = 0x18,
|
|
|
|
Rresp3 = 0x1C,
|
|
|
|
Rdat0 = 0x20,
|
|
|
|
Rdat1 = 0x22,
|
|
|
|
Rpres = 0x24,
|
|
|
|
Rhc = 0x28,
|
|
|
|
Rpwr = 0x29,
|
|
|
|
Rbgc = 0x2A,
|
|
|
|
Rwkc = 0x2B,
|
|
|
|
Rclc = 0x2C,
|
|
|
|
Rtmc = 0x2E,
|
|
|
|
Rsrst = 0x2F,
|
|
|
|
Rnis = 0x30,
|
|
|
|
Reis = 0x32,
|
|
|
|
Rnie = 0x34,
|
|
|
|
Reie = 0x36,
|
|
|
|
Rnise = 0x38,
|
|
|
|
Reise = 0x3A,
|
|
|
|
Ra12 = 0x3C,
|
|
|
|
Rcap = 0x40,
|
|
|
|
Rrcap = 0x44,
|
|
|
|
Rxcap = 0x48,
|
|
|
|
Rrxcap = 0x4C,
|
|
|
|
Rfea12 = 0x50,
|
|
|
|
Rfeei = 0x52,
|
|
|
|
Radmasr = 0x54,
|
|
|
|
Radmaba = 0x58,
|
|
|
|
Rsists = 0xFC,
|
|
|
|
Rhcver = 0xFE,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* sts bits */
|
|
|
|
enum {
|
|
|
|
Seint = 1<<15,
|
|
|
|
Snint = 1<<8,
|
|
|
|
Srem = 1<<7,
|
|
|
|
Sins = 1<<6,
|
|
|
|
Srrdy = 1<<5,
|
|
|
|
Swrdy = 1<<4,
|
|
|
|
Sdint = 1<<3,
|
|
|
|
Sbge = 1<<2,
|
|
|
|
Strac = 1<<1,
|
|
|
|
Scmdc = 1<<0,
|
|
|
|
|
|
|
|
Smask = 0x81ff,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* err bits */
|
|
|
|
enum {
|
|
|
|
Ea12 = 1<<8,
|
|
|
|
Elimit = 1<<7,
|
|
|
|
Edebit = 1<<6,
|
|
|
|
Edcrc = 1<<5,
|
|
|
|
Edtmo = 1<<4,
|
|
|
|
Ecidx = 1<<3,
|
|
|
|
Ecebit = 1<<2,
|
|
|
|
Eccrc = 1<<1,
|
|
|
|
Ectmo = 1<<0,
|
|
|
|
|
|
|
|
Emask = 0x1ff,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* present bits */
|
|
|
|
enum {
|
|
|
|
Plsig = 1<<24,
|
|
|
|
Pldat3 = 1<<23,
|
|
|
|
Pldat2 = 1<<22,
|
|
|
|
Pldat1 = 1<<21,
|
|
|
|
Pldat0 = 1<<20,
|
|
|
|
|
|
|
|
Ppswit = 1<<19,
|
|
|
|
Pcrddt = 1<<18,
|
|
|
|
Pcrdst = 1<<17,
|
|
|
|
Pcrdin = 1<<16,
|
|
|
|
|
|
|
|
Pbufrd = 1<<11,
|
|
|
|
Pbufwr = 1<<10,
|
|
|
|
Ptrard = 1<<9,
|
|
|
|
Ptrawr = 1<<8,
|
|
|
|
Pdat = 1<<2,
|
|
|
|
|
|
|
|
Pinhbc = 1<<1,
|
|
|
|
Pinhbd = 1<<0,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Rmode bits */
|
|
|
|
enum {
|
|
|
|
Mblk = 1<<5,
|
|
|
|
Mrd = 1<<4,
|
|
|
|
Mwr = 0<<4,
|
|
|
|
Ma12 = 1<<2,
|
|
|
|
Mcnt = 1<<1,
|
|
|
|
Mdma = 1<<0,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* command bits */
|
|
|
|
enum {
|
|
|
|
Abort = 3<<6,
|
|
|
|
Isdata = 1<<5,
|
|
|
|
Ixchken = 1<<4,
|
|
|
|
Crcchken = 1<<3,
|
|
|
|
|
|
|
|
Respmask = 3,
|
|
|
|
Respnone = 0,
|
|
|
|
Resp48busy = 3,
|
|
|
|
Resp48 = 2,
|
|
|
|
Resp136 = 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* fake for cmdinfo */
|
|
|
|
enum {
|
|
|
|
Blkcnten = Mblk << 8,
|
|
|
|
Multiblock = Mcnt << 8,
|
|
|
|
Card2host = Mrd << 8,
|
|
|
|
Host2card = Mwr << 8,
|
|
|
|
};
|
|
|
|
|
2015-05-11 03:27:05 +00:00
|
|
|
static int cmdinfo[64] = {
|
2013-07-13 23:33:07 +00:00
|
|
|
[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,
|
|
|
|
[18] Resp48 | Isdata | Card2host | Multiblock | Blkcnten | Ixchken | Crcchken,
|
|
|
|
[24] Resp48 | Isdata | Host2card | Ixchken | Crcchken,
|
|
|
|
[25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken,
|
|
|
|
[41] Resp48,
|
|
|
|
[55] Resp48 | Ixchken | Crcchken,
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct Ctlr Ctlr;
|
|
|
|
struct Ctlr {
|
|
|
|
Lock;
|
|
|
|
|
|
|
|
Pcidev *pdev;
|
|
|
|
u8int *mmio;
|
|
|
|
|
|
|
|
int change;
|
|
|
|
|
|
|
|
u32int waitsts;
|
|
|
|
u32int waitmsk;
|
|
|
|
Rendez r;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
int bcount;
|
|
|
|
int bsize;
|
|
|
|
} io;
|
|
|
|
};
|
|
|
|
|
|
|
|
static Ctlr pmmc[1];
|
|
|
|
|
|
|
|
#define CR8(c, off) *((u8int*)(c->mmio + off))
|
|
|
|
#define CR16(c, off) *((u16int*)(c->mmio + off))
|
|
|
|
#define CR32(c, off) *((u32int*)(c->mmio + off))
|
|
|
|
|
|
|
|
static void
|
|
|
|
mmcinterrupt(Ureg*, void *arg)
|
|
|
|
{
|
|
|
|
u16int nis, eis;
|
|
|
|
Ctlr *c;
|
|
|
|
|
|
|
|
c = arg;
|
|
|
|
nis = CR16(c, Rnis);
|
|
|
|
if((nis & Smask) == 0)
|
|
|
|
return; /* not for us */
|
|
|
|
|
|
|
|
CR16(c, Rnis) = nis; /* ack */
|
|
|
|
ilock(c);
|
|
|
|
eis = 0;
|
|
|
|
if((nis & Seint) != 0){
|
|
|
|
eis = CR16(c, Reis);
|
|
|
|
CR16(c, Reis) = eis; /* ack */
|
|
|
|
}
|
|
|
|
if((nis & Snint) != 0)
|
|
|
|
CR16(c, Rnie) |= Snint; /* ack */
|
|
|
|
if((nis & (Srem|Sins)) != 0)
|
|
|
|
c->change = 1;
|
|
|
|
c->waitsts |= nis | (eis << 16);
|
|
|
|
if((c->waitsts & c->waitmsk) != 0)
|
|
|
|
wakeup(&c->r);
|
|
|
|
iunlock(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pmmcinit(void)
|
|
|
|
{
|
|
|
|
Pcidev *p;
|
|
|
|
|
|
|
|
p = nil;
|
|
|
|
while((p = pcimatch(p, 0, 0)) != nil){
|
2014-04-26 16:22:17 +00:00
|
|
|
if(p->ccrb == 8 && p->ccru == 5)
|
|
|
|
break;
|
2013-09-14 23:24:08 +00:00
|
|
|
if(p->vid == 0x1180){ /* Ricoh */
|
|
|
|
if(p->did == 0xe822) /* 5U822 SD/MMC */
|
|
|
|
break;
|
|
|
|
if(p->did == 0xe823) /* 5U823 SD/MMC */
|
|
|
|
break;
|
|
|
|
}
|
2013-07-13 23:33:07 +00:00
|
|
|
}
|
|
|
|
|
2020-06-06 14:19:25 +00:00
|
|
|
if(p == nil || p->mem[0].size < 256 || (p->mem[0].bar & 1) != 0)
|
2013-07-13 23:33:07 +00:00
|
|
|
return -1;
|
|
|
|
|
2018-10-07 20:28:21 +00:00
|
|
|
pmmc->mmio = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
|
|
|
|
if(pmmc->mmio == nil)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
pmmc->pdev = p;
|
|
|
|
pcienable(p);
|
|
|
|
|
2013-07-13 23:33:07 +00:00
|
|
|
if(p->did == 0x1180 && p->vid == 0xe823){ /* Ricoh */
|
|
|
|
/* Enable SD2.0 mode. */
|
|
|
|
pcicfgw8(p, 0xf9, 0xfc);
|
|
|
|
pcicfgw8(p, 0x150, 0x10);
|
|
|
|
pcicfgw8(p, 0xf9, 0x00);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some SD/MMC cards don't work with the default base
|
|
|
|
* clock frequency of 200MHz. Lower it to 50Hz.
|
|
|
|
*/
|
|
|
|
pcicfgw8(p, 0xfc, 0x01);
|
|
|
|
pcicfgw8(p, 0xe1, 50);
|
|
|
|
pcicfgw8(p, 0xfc, 0x00);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pmmcinquiry(char *inquiry, int inqlen)
|
|
|
|
{
|
|
|
|
return snprint(inquiry, inqlen, "MMC Host Controller");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
softreset(Ctlr *c, int all)
|
|
|
|
{
|
|
|
|
int i, m;
|
|
|
|
|
|
|
|
m = all ? 1 : 6;
|
|
|
|
CR8(c, Rsrst) = m;
|
|
|
|
for(i=10; i>=0; i--){
|
|
|
|
if((CR8(c, Rsrst) & m) == 0)
|
|
|
|
break;
|
|
|
|
delay(10);
|
|
|
|
CR8(c, Rsrst) = 0;
|
|
|
|
}
|
|
|
|
if(i < 0) iprint("mmc: didnt reset\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
setpower(Ctlr *c, int on)
|
|
|
|
{
|
|
|
|
enum {
|
|
|
|
Vcap18 = 1<<26, Vset18 = 0x05,
|
|
|
|
Vcap30 = 1<<25, Vset30 = 0x06,
|
|
|
|
Vcap33 = 1<<24, Vset33 = 0x07,
|
|
|
|
};
|
|
|
|
u32int cap, v;
|
|
|
|
|
|
|
|
cap = CR32(c, Rcap);
|
|
|
|
v = Vset18;
|
|
|
|
if(cap & Vcap30)
|
|
|
|
v = Vset30;
|
|
|
|
if(cap & Vcap33)
|
|
|
|
v = Vset33;
|
|
|
|
CR8(c, Rpwr) = on ? ((v<<1) | 1) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
setclkfreq(Ctlr *c, int khz)
|
|
|
|
{
|
|
|
|
u32int caps, intfreq;
|
|
|
|
int i, div;
|
|
|
|
|
|
|
|
if(khz == 0){
|
|
|
|
CR16(c, Rclc) |= ~4; /* sd clock disable */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
caps = CR32(c, Rcap);
|
|
|
|
intfreq = 1000*((caps >> 8) & 0x3f);
|
|
|
|
for(div = 1; div <= 256; div <<= 1){
|
|
|
|
if((intfreq / div) <= khz){
|
|
|
|
div >>= 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CR16(c, Rclc) = 0;
|
|
|
|
CR16(c, Rclc) = div<<8;
|
|
|
|
CR16(c, Rclc) |= 1; /* int clock enable */
|
|
|
|
for(i=1000; i>=0; i--){
|
|
|
|
if(CR16(c, Rclc) & 2) /* int clock stable */
|
|
|
|
break;
|
|
|
|
delay(10);
|
|
|
|
}
|
|
|
|
if(i < 0) iprint("mmc: clock didnt stabilize\n");
|
|
|
|
CR16(c, Rclc) |= 4; /* sd clock enable */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
resetctlr(Ctlr *c)
|
|
|
|
{
|
|
|
|
u32int m;
|
|
|
|
|
|
|
|
ilock(c);
|
|
|
|
CR16(c, Rnise) = 0; /* interrupts off */
|
|
|
|
|
|
|
|
c->change = 0;
|
|
|
|
c->waitmsk = c->waitsts = 0;
|
|
|
|
softreset(c, 1);
|
|
|
|
|
|
|
|
/* set timeout */
|
|
|
|
CR8(c, Rtmc) = 0x0e;
|
|
|
|
|
|
|
|
m = Srem | Sins | Srrdy | Swrdy | Sdint | Sbge | Strac | Scmdc;
|
|
|
|
CR16(c, Rnie) = m;
|
|
|
|
CR16(c, Reie) = Emask;
|
|
|
|
CR16(c, Rnise) = m;
|
|
|
|
CR16(c, Reise) = Emask;
|
|
|
|
|
|
|
|
setpower(c, 1);
|
|
|
|
setclkfreq(c, 400);
|
|
|
|
iunlock(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
waitcond(void *arg)
|
|
|
|
{
|
|
|
|
Ctlr *c;
|
|
|
|
|
|
|
|
c = arg;
|
|
|
|
return (c->waitsts & c->waitmsk) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32int
|
|
|
|
intrwait(Ctlr *c, u32int mask, int tmo)
|
|
|
|
{
|
|
|
|
u32int status;
|
|
|
|
|
2013-07-26 02:37:32 +00:00
|
|
|
ilock(c);
|
2013-07-13 23:33:07 +00:00
|
|
|
c->waitmsk = Seint | mask;
|
2013-07-26 02:37:32 +00:00
|
|
|
iunlock(c);
|
2013-07-13 23:33:07 +00:00
|
|
|
do {
|
|
|
|
if(!waserror()){
|
|
|
|
tsleep(&c->r, waitcond, c, 100);
|
|
|
|
poperror();
|
|
|
|
}
|
|
|
|
mmcinterrupt(nil, c);
|
|
|
|
if(waitcond(c))
|
|
|
|
break;
|
|
|
|
tmo -= 100;
|
|
|
|
} while(tmo > 0);
|
|
|
|
ilock(c);
|
2013-07-26 02:37:32 +00:00
|
|
|
c->waitmsk = 0;
|
2013-07-13 23:33:07 +00:00
|
|
|
status = c->waitsts;
|
|
|
|
c->waitsts &= ~(status & mask);
|
|
|
|
if((status & mask) == 0 || (status & Seint) != 0){
|
|
|
|
/* abort command on timeout/error interrupt */
|
|
|
|
softreset(c, 0);
|
|
|
|
c->waitsts = 0;
|
|
|
|
status = 0;
|
|
|
|
}
|
|
|
|
iunlock(c);
|
|
|
|
|
2013-07-26 02:37:32 +00:00
|
|
|
return status & mask;
|
2013-07-13 23:33:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
pmmcenable(void)
|
|
|
|
{
|
|
|
|
Pcidev *p;
|
|
|
|
Ctlr *c;
|
|
|
|
|
|
|
|
c = pmmc;
|
|
|
|
p = c->pdev;
|
|
|
|
resetctlr(c);
|
|
|
|
intrenable(p->intl, mmcinterrupt, c, p->tbdf, "mmc");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pmmccmd(u32int cmd, u32int arg, u32int *resp)
|
|
|
|
{
|
|
|
|
u32int status;
|
|
|
|
int i, mode;
|
|
|
|
Ctlr *c;
|
|
|
|
|
|
|
|
if(cmd >= nelem(cmdinfo) || cmdinfo[cmd] == 0)
|
|
|
|
error(Egreg);
|
|
|
|
mode = cmdinfo[cmd] >> 8;
|
|
|
|
cmd = (cmd << 8) | (cmdinfo[cmd] & 0xFF);
|
|
|
|
|
|
|
|
c = pmmc;
|
|
|
|
|
|
|
|
if(c->change)
|
|
|
|
resetctlr(c);
|
|
|
|
if((CR32(c, Rpres) & Pcrdin) == 0)
|
|
|
|
error("no card");
|
|
|
|
|
|
|
|
status = Pinhbc;
|
|
|
|
if((cmd & Isdata) != 0 || (cmd & Respmask) == Resp48busy)
|
|
|
|
status |= Pinhbd;
|
|
|
|
for(i=1000; (CR32(c, Rpres) & status) != 0 && i>=0; i--)
|
|
|
|
delay(1);
|
|
|
|
if(i < 0)
|
|
|
|
error(Eio);
|
|
|
|
|
|
|
|
ilock(c);
|
|
|
|
if((mode & (Mcnt|Mblk)) == (Mcnt|Mblk)){
|
|
|
|
CR16(c, Rbsize) = c->io.bsize;
|
|
|
|
CR16(c, Rbcount) = c->io.bcount;
|
|
|
|
}
|
|
|
|
CR32(c, Rarg) = arg;
|
|
|
|
CR16(c, Rmode) = mode;
|
|
|
|
CR16(c, Rcmd) = cmd;
|
|
|
|
iunlock(c);
|
|
|
|
|
|
|
|
if(!intrwait(c, Scmdc, 1000))
|
|
|
|
error(Eio);
|
|
|
|
|
|
|
|
switch(cmd & Respmask){
|
|
|
|
case Resp48busy:
|
|
|
|
resp[0] = CR32(c, Rresp0);
|
|
|
|
if(!intrwait(c, Strac, 3000))
|
|
|
|
error(Eio);
|
|
|
|
break;
|
|
|
|
case Resp48:
|
|
|
|
resp[0] = CR32(c, Rresp0);
|
|
|
|
break;
|
|
|
|
case Resp136:
|
|
|
|
resp[0] = CR32(c, Rresp0)<<8;
|
|
|
|
resp[1] = CR32(c, Rresp0)>>24 | CR32(c, Rresp1)<<8;
|
|
|
|
resp[2] = CR32(c, Rresp1)>>24 | CR32(c, Rresp2)<<8;
|
|
|
|
resp[3] = CR32(c, Rresp2)>>24 | CR32(c, Rresp3)<<8;
|
|
|
|
break;
|
|
|
|
case Respnone:
|
|
|
|
resp[0] = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd >>= 8;
|
|
|
|
if(cmd == 0x06){ /* buswidth */
|
|
|
|
switch(arg){
|
|
|
|
case 0:
|
|
|
|
CR8(c, Rhc) &= ~2;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
CR8(c, Rhc) |= 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pmmciosetup(int write, void *buf, int bsize, int bcount)
|
|
|
|
{
|
|
|
|
Ctlr *c;
|
|
|
|
|
|
|
|
USED(write);
|
|
|
|
USED(buf);
|
|
|
|
|
|
|
|
if(bsize == 0 || (bsize & 3) != 0)
|
|
|
|
error(Egreg);
|
|
|
|
|
|
|
|
c = pmmc;
|
|
|
|
c->io.bsize = bsize;
|
|
|
|
c->io.bcount = bcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
readblock(Ctlr *c, uchar *buf, int len)
|
|
|
|
{
|
|
|
|
for(len >>= 2; len > 0; len--){
|
|
|
|
*((u32int*)buf) = CR32(c, Rdat0);
|
|
|
|
buf += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
writeblock(Ctlr *c, uchar *buf, int len)
|
|
|
|
{
|
|
|
|
for(len >>= 2; len > 0; len--){
|
|
|
|
CR32(c, Rdat0) = *((u32int*)buf);
|
|
|
|
buf += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pmmcio(int write, uchar *buf, int len)
|
|
|
|
{
|
|
|
|
Ctlr *c;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
c = pmmc;
|
|
|
|
if(len != c->io.bsize*c->io.bcount)
|
|
|
|
error(Egreg);
|
|
|
|
while(len > 0){
|
|
|
|
if(!intrwait(c, write ? Swrdy : Srrdy, 3000))
|
|
|
|
error(Eio);
|
|
|
|
n = len;
|
|
|
|
if(n > c->io.bsize)
|
|
|
|
n = c->io.bsize;
|
|
|
|
if(write)
|
|
|
|
writeblock(c, buf, n);
|
|
|
|
else
|
|
|
|
readblock(c, buf, n);
|
|
|
|
len -= n;
|
|
|
|
buf += n;
|
|
|
|
}
|
|
|
|
if(!intrwait(c, Strac, 1000))
|
|
|
|
error(Eio);
|
|
|
|
}
|
|
|
|
|
|
|
|
SDio sdio = {
|
|
|
|
"pmmc",
|
|
|
|
pmmcinit,
|
|
|
|
pmmcenable,
|
|
|
|
pmmcinquiry,
|
|
|
|
pmmccmd,
|
|
|
|
pmmciosetup,
|
|
|
|
pmmcio,
|
|
|
|
};
|