Browse Source

Plan 9 from Bell Labs 2012-11-14

David du Colombier 11 years ago
parent
commit
9a8089dbc6

+ 2 - 0
sys/src/9/kw/fns.h

@@ -113,6 +113,8 @@ char*	getconf(char*);
 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);

+ 1 - 0
sys/src/9/kw/plug

@@ -57,6 +57,7 @@ misc
 	rdb
 	coproc
 	sdaoe		sdscsi
+	sdmmc		sdio
 	softfpu
 	syscall
 	uartkw

+ 580 - 0
sys/src/9/kw/sdio.c

@@ -0,0 +1,580 @@
+/*
+ * 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,
+};

+ 17 - 0
sys/src/9/port/sd.h

@@ -3,6 +3,7 @@
  */
 typedef struct SDev SDev;
 typedef struct SDifc SDifc;
+typedef struct SDio SDio;
 typedef struct SDpart SDpart;
 typedef struct SDperm SDperm;
 typedef struct SDreq SDreq;
@@ -145,6 +146,22 @@ enum {
 #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*);
 extern void sdaddconf(SDunit*);

+ 315 - 0
sys/src/9/port/sdmmc.c

@@ -0,0 +1,315 @@
+/*
+ * mmc / sd memory card
+ *
+ *
+ * Copyright © 2012 Richard Miller <r.miller@acm.org>
+ *
+ * Assumes only one card on the bus
+ */
+
+#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 CSD(end, start)	rbits(csd, start, (end)-(start)+1)
+
+typedef struct Ctlr Ctlr;
+
+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,
+};
+
+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<<len) - 1);
+	else
+		return v;
+}
+
+static void
+identify(SDunit *unit, u32int *csd)
+{
+	uint csize, mult;
+
+	unit->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)
+{
+	int n;
+	Ctlr *ctl;
+
+	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)
+{
+	int hcs, i;
+	u32int r[4];
+	Ctlr *ctl;
+	SDio *io;
+
+	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<<Rcashift, r);
+		poperror();
+		return 1;
+	}
+	io->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<<Rcashift, r);
+	memmove(ctl->csd, r, sizeof ctl->csd);
+	identify(unit, ctl->csd);
+	io->cmd(SELECT_CARD, ctl->rca<<Rcashift, r);
+	io->cmd(SET_BLOCKLEN, unit->secsize, r);
+	io->cmd(APP_CMD, ctl->rca<<Rcashift, r);
+	io->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)
+{
+	int len, tries;
+	ulong b;
+	u32int r[4];
+	uchar *buf;
+	Ctlr *ctl;
+	SDio *io;
+
+	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,
+};

+ 2 - 0
sys/src/cmd/usb/ether/asix.c

@@ -474,7 +474,9 @@ asixreset(Ether *ether)
 				return -1;
 			}
 			deprint(2, "%s: asix reset done\n", argv0);
+			ether->name = "asix";
 			ether->aux = emallocz(sizeof(Buf), 1);
+			ether->bufsize = Hdrsize+Maxpkt;
 			ether->bread = asixbread;
 			ether->bwrite = asixbwrite;
 			ether->free = asixfree;

+ 36 - 3
sys/src/cmd/usb/ether/ether.c

@@ -174,7 +174,7 @@ allocbuf(Ether *e)
 		qunlock(e);
 	}
 	if(bp == nil){
-		fprint(2, "%s: blocked waiting for allocbuf\n", argv0);
+		deprint(2, "%s: blocked waiting for allocbuf\n", argv0);
 		bp = recvp(e->bc);
 	}
 	bp->rp = bp->data + Hdrsize;
@@ -802,7 +802,7 @@ fswrite(Usbfs *fs, Fid *fid, void *data, long count, vlong)
 		if(e->nblock == 0)
 			sendp(e->wc, bp);
 		else if(nbsendp(e->wc, bp) == 0){
-			fprint(2, "%s: (out) packet lost\n", argv0);
+			deprint(2, "%s: (out) packet lost\n", argv0);
 			e->noerrs++;
 			freebuf(e, bp);
 		}
@@ -1038,7 +1038,7 @@ etherreadproc(void *a)
 					dbp->type = bp->type;
 				}
 				if(nbsendp(e->conns[i]->rc, dbp) == 0){
-					fprint(2, "%s: (in) packet lost\n", argv0);
+					deprint(2, "%s: (in) packet lost\n", argv0);
 					e->nierrs++;
 					freebuf(e, dbp);
 				}
@@ -1142,6 +1142,34 @@ etherinit(Ether *e, int *ei, int *eo)
 	return -1;
 }
 
+static int
+kernelproxy(Ether *e)
+{
+	int ctlfd, n;
+	char eaddr[13];
+
+	ctlfd = open("#l0/ether0/clone", ORDWR);
+	if(ctlfd < 0){
+		deprint(2, "%s: etherusb bind #l0: %r\n", argv0);
+		return -1;
+	}
+	close(e->epin->dfd);
+	close(e->epout->dfd);
+	seprintaddr(eaddr, eaddr+sizeof(eaddr), e->addr);
+	n = fprint(ctlfd, "bind %s #u/usb/ep%d.%d/data #u/usb/ep%d.%d/data %s %d %d",
+		e->name, e->dev->id, e->epin->id, e->dev->id, e->epout->id,
+		eaddr, e->bufsize, e->epout->maxpkt);
+	if(n < 0){
+		deprint(2, "%s: etherusb bind #l0: %r\n", argv0);
+		opendevdata(e->epin, OREAD);
+		opendevdata(e->epout, OWRITE);
+		close(ctlfd);
+		return -1;
+	}
+	close(ctlfd);
+	return 0;
+}
+
 int
 ethermain(Dev *dev, int argc, char **argv)
 {
@@ -1174,6 +1202,7 @@ ethermain(Dev *dev, int argc, char **argv)
 	e->dev = dev;
 	dev->free = etherdevfree;
 	memmove(e->addr, ea, Eaddrlen);
+	e->name = "cdc";
 
 	for(i = 0; i < nelem(ethers); i++)
 		if(ethers[i](e) == 0)
@@ -1188,9 +1217,13 @@ ethermain(Dev *dev, int argc, char **argv)
 		e->bwrite = etherbwrite;
 	if(e->bread == nil)
 		e->bread = etherbread;
+	if(e->bufsize == 0)
+		e->bufsize = Maxpkt;
 
 	if(openeps(e, epin, epout) < 0)
 		return -1;
+	if(kernelproxy(e) == 0)
+		return 0;
 	e->fs = etherfs;
 	snprint(e->fs.name, sizeof(e->fs.name), "etherU%d", devid);
 	e->fs.dev = dev;

+ 2 - 0
sys/src/cmd/usb/ether/ether.h

@@ -58,6 +58,8 @@ struct Etherops
 	int	(*multicast)(Ether*, uchar*, int);
 	char*	(*seprintstats)(char*, char*, Ether*);
 	void	(*free)(Ether*);
+	int	bufsize;
+	char	*name;
 	void*	aux;
 };
 

+ 12 - 4
sys/src/cmd/usb/ether/smsc.c

@@ -15,7 +15,7 @@ enum {
 	Resettime	= 1000,
 	E2pbusytime	= 1000,
 	Afcdefault	= 0xF830A1,
-//	Hsburst		= 37,
+//	Hsburst		= 37,	/* from original linux driver */
 	Hsburst		= 8,
 	Fsburst		= 129,
 	Defbulkdly	= 0x2000,
@@ -214,7 +214,7 @@ getmac(Dev *d, uchar buf[])
 	if(eepromr(d, MACoffset, ea, Eaddrlen) < 0)
 		return -1;
 	for(i = 0; i < Eaddrlen; i++)
-		if(ea[i] != 0){
+		if(ea[i] != 0 && ea[i] != 0xFF){
 			memmove(buf, ea, Eaddrlen);
 			break;
 		}
@@ -275,7 +275,7 @@ smscbread(Ether *e, Buf *bp)
 	if(rbp->ndata < 4){
 		rbp->rp = rbp->data;
 		rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? Hsburst*512:
-			sizeof(rbp->data));
+			Maxpkt);
 		if(rbp->ndata < 0)
 			return -1;
 	}
@@ -342,7 +342,15 @@ smscreset(Ether *ether)
 				return -1;
 			}
 			deprint(2, "%s: smsc reset done\n", argv0);
-			ether->aux = emallocz(sizeof(Buf) + Hsburst*512 - Maxpkt, 1);
+			ether->name = "smsc";
+			if(Doburst){
+				ether->bufsize = Hsburst*512;
+				ether->aux = emallocz(sizeof(Buf) +
+					ether->bufsize - Maxpkt, 1);
+			}else{
+				ether->bufsize = Maxpkt;
+				ether->aux = emallocz(sizeof(Buf), 1);
+			}
 			ether->free = smscfree;
 			ether->bread = smscbread;
 			ether->bwrite = smscbwrite;

+ 26 - 4
sys/src/cmd/usb/kb/kb.c

@@ -21,6 +21,7 @@ enum
 {
 	Awakemsg= 0xdeaddead,
 	Diemsg	= 0xbeefbeef,
+	Dwcidle	= 8,
 };
 
 typedef struct KDev KDev;
@@ -31,6 +32,7 @@ struct KDev
 	Dev*	dev;		/* usb device*/
 	Dev*	ep;		/* endpoint to get events */
 	Kin*	in;		/* used to send events to kernel */
+	int	idle;		/* min time between reports (× 4ms) */
 	Channel*repeatc;	/* only for keyboard */
 	int	accel;		/* only for mouse */
 	int	bootp;		/* has associated keyboard */
@@ -124,13 +126,17 @@ static int ptrrepvals(KDev *kd, Chain *ch, int *px, int *py, int *pb);
 static int
 setbootproto(KDev* f, int eid, uchar *, int)
 {
-	int r, id;
+	int nr, r, id;
 
 	f->ptrvals = ptrbootpvals;
 	r = Rh2d|Rclass|Riface;
 	dprint(2, "setting boot protocol\n");
 	id = f->dev->usb->ep[eid]->iface->id;
-	return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
+	nr = usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
+	if(nr < 0)
+		return -1;
+	usbcmd(f->dev, r, Setidle, f->idle<<8, id, nil, 0);
+	return nr;
 }
 
 static uchar ignoredesc[128];
@@ -151,7 +157,7 @@ setfirstconfig(KDev* f, int eid, uchar *desc, int descsz)
 	if(nr < 0)
 		return -1;
 	r = Rh2d | Rclass | Riface;
-	nr=usbcmd(f->dev,   r, Setidle,  0, id, nil, 0);
+	nr = usbcmd(f->dev, r, Setidle, f->idle<<8, id, nil, 0);
 	if(nr < 0)
 		return -1;
 	r = Rd2h | Rstd | Riface;
@@ -336,6 +342,7 @@ ptrwork(void* a)
 	Chain ch;
 	KDev* f = a;
 
+	threadsetname("ptr %s", f->in->name);
 	hipri = nerrs = 0;
 	ptrfd = f->ep->dfd;
 	mfd = f->in->fd;
@@ -426,6 +433,7 @@ repeatproc(void* a)
 	ulong l, t, i;
 	uchar esc1, sc;
 
+	threadsetname("kbd repeat");
 	/*
 	 * too many jumps here.
 	 * Rewrite instead of debug, if needed.
@@ -543,6 +551,7 @@ kbdwork(void *a)
 	char err[128];
 	KDev *f = a;
 
+	threadsetname("kbd %s", f->in->name);
 	kbdfd = f->ep->dfd;
 
 	if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf)
@@ -608,7 +617,7 @@ static void
 kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), KDev *kd)
 {
 	uchar desc[128];
-	int res;
+	int n, res;
 
 	qlock(&inlck);
 	if(in->fd < 0){
@@ -630,6 +639,19 @@ kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), KDev *kd)
 		fprint(2, "kb: %s: openep %d: %r\n", d->dir, ep->id);
 		return;
 	}
+	if(in == &kbdin){
+		/*
+		 * DWC OTG controller misses some split transaction inputs.
+		 * Set nonzero idle time to return more frequent reports
+		 * of keyboard state, to avoid losing key up/down events.
+		 */
+		n = read(d->cfd, desc, sizeof desc - 1);
+		if(n > 0){
+			desc[n] = 0;
+			if(strstr((char*)desc, "dwcotg") != nil)
+				kd->idle = Dwcidle;
+		}
+	}
 	if(!kd->bootp)
 		res= setfirstconfig(kd, ep->id, desc, sizeof desc);
 	if(res > 0)

+ 2 - 0
sys/src/cmd/usb/usbd/dev.c

@@ -65,6 +65,8 @@ devmatch(Devtab *dt, Usbdev *d)
 	int c;
 	Conf *cp;
 
+	if(dt->noauto)
+		return 0;
 	if(dt->vid != -1 && d->vid != dt->vid)
 		return 0;
 	if(dt->did != -1 && d->did != dt->did)