shithub: tpi

Download patch

ref: fc250ff59bf422d2494c4d2763edb9767d7e91f0
author: glenda <[email protected]>
date: Sat Apr 9 05:40:46 EDT 2022

initial commit

diff: cannot open b/v1/9/blink/bcm//null: file does not exist: 'b/v1/9/blink/bcm//null' diff: cannot open b/v1/9/blink//null: file does not exist: 'b/v1/9/blink//null' diff: cannot open b/v1/9/emmc/bcm//null: file does not exist: 'b/v1/9/emmc/bcm//null' diff: cannot open b/v1/9/emmc/port//null: file does not exist: 'b/v1/9/emmc/port//null' diff: cannot open b/v1/9/emmc//null: file does not exist: 'b/v1/9/emmc//null' diff: cannot open b/v1/9/i2c/bcm//null: file does not exist: 'b/v1/9/i2c/bcm//null' diff: cannot open b/v1/9/i2c/bcm64//null: file does not exist: 'b/v1/9/i2c/bcm64//null' diff: cannot open b/v1/9/i2c//null: file does not exist: 'b/v1/9/i2c//null' diff: cannot open b/v1/9//null: file does not exist: 'b/v1/9//null' diff: cannot open b/v1//null: file does not exist: 'b/v1//null'
--- /dev/null
+++ b/v1/9/blink/bcm/devarch.c
@@ -1,0 +1,231 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+enum {
+	Qdir = 0,
+	Qbase,
+
+	Qmax = 16,
+};
+
+typedef long Rdwrfn(Chan*, void*, long, vlong);
+
+static Rdwrfn *readfn[Qmax];
+static Rdwrfn *writefn[Qmax];
+
+static Dirtab archdir[Qmax] = {
+	".",		{ Qdir, 0, QTDIR },	0,	0555,
+};
+
+Lock archwlock;	/* the lock is only for changing archdir */
+int narchdir = Qbase;
+
+/*
+ * Add a file to the #P listing.  Once added, you can't delete it.
+ * You can't add a file with the same name as one already there,
+ * and you get a pointer to the Dirtab entry so you can do things
+ * like change the Qid version.  Changing the Qid path is disallowed.
+ */
+Dirtab*
+addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
+{
+	int i;
+	Dirtab d;
+	Dirtab *dp;
+
+	memset(&d, 0, sizeof d);
+	strcpy(d.name, name);
+	d.perm = perm;
+
+	lock(&archwlock);
+	if(narchdir >= Qmax){
+		unlock(&archwlock);
+		return nil;
+	}
+
+	for(i=0; i<narchdir; i++)
+		if(strcmp(archdir[i].name, name) == 0){
+			unlock(&archwlock);
+			return nil;
+		}
+
+	d.qid.path = narchdir;
+	archdir[narchdir] = d;
+	readfn[narchdir] = rdfn;
+	writefn[narchdir] = wrfn;
+	dp = &archdir[narchdir++];
+	unlock(&archwlock);
+
+	return dp;
+}
+
+static Chan*
+archattach(char* spec)
+{
+	return devattach('P', spec);
+}
+
+Walkqid*
+archwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
+}
+
+static int
+archstat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, archdir, narchdir, devgen);
+}
+
+static Chan*
+archopen(Chan* c, int omode)
+{
+	return devopen(c, omode, archdir, narchdir, devgen);
+}
+
+static void
+archclose(Chan*)
+{
+}
+
+static long
+archread(Chan *c, void *a, long n, vlong offset)
+{
+	Rdwrfn *fn;
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, archdir, narchdir, devgen);
+
+	default:
+		if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
+			return fn(c, a, n, offset);
+		error(Eperm);
+		break;
+	}
+
+	return 0;
+}
+
+static long
+archwrite(Chan *c, void *a, long n, vlong offset)
+{
+	Rdwrfn *fn;
+
+	if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
+		return fn(c, a, n, offset);
+	error(Eperm);
+
+	return 0;
+}
+
+void archinit(void);
+
+Dev archdevtab = {
+	'P',
+	"arch",
+
+	devreset,
+	archinit,
+	devshutdown,
+	archattach,
+	archwalk,
+	archstat,
+	archopen,
+	devcreate,
+	archclose,
+	archread,
+	devbread,
+	archwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+static long
+cputyperead(Chan*, void *a, long n, vlong offset)
+{
+	char name[64], str[128];
+
+	cputype2name(name, sizeof name);
+	snprint(str, sizeof str, "ARM %s %d\n", name, m->cpumhz);
+	return readstr(offset, a, n, str);
+}
+
+static long
+cputempread(Chan*, void *a, long n, vlong offset)
+{
+	char str[32];
+	uint t = getcputemp();
+	snprint(str, sizeof str, "%ud.%ud\n", t/1000, t%1000);
+	return readstr(offset, a, n, str);
+}
+
+void
+archinit(void)
+{
+	addarchfile("cputype", 0444, cputyperead, nil);
+	addarchfile("cputemp", 0444, cputempread, nil);
+}
+
+void
+uartconsinit(void)
+{
+	extern PhysUart *physuart[];
+	char *p, *cmd;
+	Uart *uart;
+	int i, n;
+
+	if((p = getconf("console")) == nil)
+		return;
+	i = strtoul(p, &cmd, 0);
+	if(p == cmd)
+		return;
+	/* we only have two possible uarts, the pl011 and aux */
+	for(n = 0; physuart[n] != nil; n++)
+		;
+	if(i < 0 || i >= n)
+		return;
+	uart = physuart[i]->pnp();
+	if(!uart->enabled)
+		(*uart->phys->enable)(uart, 0);
+	uartctl(uart, "l8 pn s1");
+	if(*cmd != '\0')
+		uartctl(uart, cmd);
+	uart->console = 1;
+	consuart = uart;
+	uartputs(kmesg.buf, kmesg.n);
+}
+
+void
+okay(int on)
+{
+	static int first;
+	static int okled, polarity;
+	char *p;
+
+	if(!first++){
+		p = getconf("bcm2709.disk_led_gpio");
+		if(p == nil)
+			p = getconf("bcm2708.disk_led_gpio");
+		if(p != nil)
+			okled = strtol(p, 0, 0);
+		else
+			okled = 'v';
+		p = getconf("bcm2709.disk_led_active_low");
+		if(p == nil)
+			p = getconf("bcm2708.disk_led_active_low");
+		polarity = (p == nil || *p == '1');
+		if(okled != 'v')
+			gpiosel(okled, Output);
+	}
+	if(okled == 'v')
+		egpset(1, on);
+	else if(okled != 0)
+		gpioout(okled, on^polarity);
+}
--- /dev/null
+++ b/v1/9/emmc/bcm/emmc.c
@@ -1,0 +1,518 @@
+/*
+ * bcm2835 external mass media controller (mmc / sd host interface)
+ *
+ * Copyright © 2012 Richard Miller <[email protected]>
+ */
+
+/*
+	Not officially documented: emmc can be connected to different gpio pins
+		48-53 (SD card)
+		22-27 (P1 header)
+		34-39 (wifi - pi3 only)
+	using ALT3 function to activate the required routing
+*/
+
+#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+0x300000)
+
+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 */
+	Sendextcsd  = 8,		/* mmc ext_csd, sd send_if_cond */
+	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 */
+	Arg2			= 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,
+	Forceirpt		= 0x50>>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 */
+	Hispeed			= 1<<2,	
+	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 */
+	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,
+
+	/* 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,		/* not in Broadcom datasheet */
+	Cardinsert	= 1<<6,		/* not in Broadcom datasheet */
+	Readrdy		= 1<<5,
+	Writerdy	= 1<<4,
+	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,
+[1]  Resp48,
+[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 Ctlr Ctlr;
+
+struct Ctlr {
+	Rendez	r;
+	Rendez	cardr;
+	int	fastclock;
+	ulong	extclk;
+	int	appcmd;
+};
+
+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);
+	microdelay(emmc.fastclock ? 2: 20);
+	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 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<<Datatoshift | Clkgendiv | Clken | Clkintlen);
+	for(i = 0; i < 1000; i++){
+		delay(1);
+		if(r[Control1] & Clkstable)
+			break;
+	}
+	if(i == 1000)
+		print("emmc: can't set clock to %ud\n", freq);
+}
+
+static int
+datadone(void*)
+{
+	int i;
+
+	u32int *r = (u32int*)EMMCREGS;
+	i = r[Interrupt];
+	return i & (Datadone|Err);
+}
+
+static int
+cardintready(void*)
+{
+	int i;
+
+	u32int *r = (u32int*)EMMCREGS;
+	i = r[Interrupt];
+	return i & Cardintr;
+}
+
+static int
+emmcinit(void)
+{
+	u32int *r;
+	ulong clk;
+
+	clk = getclkrate(ClkEmmc);
+	if(clk == 0){
+		clk = Extfreq;
+		print("emmc: assuming external clock %lud Mhz\n", clk/1000000);
+	}
+	emmc.extclk = clk;
+	r = (u32int*)EMMCREGS;
+	if(0)print("emmc control %8.8ux %8.8ux %8.8ux\n",
+		r[Control0], r[Control1], r[Control2]);
+	WR(Control1, Srsthc);
+	delay(10);
+	while(r[Control1] & Srsthc)
+		;
+	WR(Control1, Srstdata);
+	delay(10);
+	WR(Control1, 0);
+	return 0;
+}
+
+static int
+emmcinquiry(char *inquiry, int inqlen)
+{
+	u32int *r;
+	uint ver;
+
+	r = (u32int*)EMMCREGS;
+	ver = r[Slotisrver] >> 16;
+	return snprint(inquiry, inqlen,
+		"Arasan eMMC SD Host Controller %2.2x Version %2.2x",
+		ver&0xFF, ver>>8);
+}
+
+static void
+emmcenable(void)
+{
+	emmcclk(Initfreq);
+	WR(Irpten, 0);
+	WR(Irptmask, ~0);
+	WR(Interrupt, ~0);
+	intrenable(IRQmmc, mmcinterrupt, nil, BUSUNKNOWN, "mmc");
+}
+
+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;
+	/*
+	 * CMD8 can be SD_SEND_IF_COND or SEND_EXT_CSD depending on arg
+	 */
+	if(cmd == Sendextcsd && arg == 0)
+		c |= Isdata|Card2host;
+	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((c & Isdata || (c & Respmask) == Resp48busy) &&
+	    r[Status] & Datinhibit){
+		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)
+{
+	USED(write);
+	USED(buf);
+	WR(Blksizecnt, bcount<<16 | bsize);
+}
+
+static void
+emmcio(int write, uchar *buf, int len)
+{
+	u32int *r;
+	int i;
+
+	r = (u32int*)EMMCREGS;
+	assert((len&3) == 0);
+	okay(1);
+	if(waserror()){
+		okay(0);
+		nexterror();
+	}
+	if(write)
+		dmastart(DmaChanEmmc, DmaDevEmmc, DmaM2D,
+			buf, &r[Data], len);
+	else
+		dmastart(DmaChanEmmc, DmaDevEmmc, DmaD2M,
+			&r[Data], buf, len);
+	if(dmawait(DmaChanEmmc) < 0)
+		error(Eio);
+	WR(Irpten, r[Irpten]|Datadone|Err);
+	tsleep(&emmc.r, datadone, 0, 3000);
+	i = r[Interrupt]&~Cardintr;
+	if((i & Datadone) == 0){
+		print("emmcio: %d timeout intr %ux stat %ux\n",
+			write, i, r[Status]);
+		WR(Interrupt, i);
+		error(Eio);
+	}
+	if(i & Err){
+		print("emmcio: %d error intr %ux stat %ux\n",
+			write, r[Interrupt], r[Status]);
+		WR(Interrupt, i);
+		error(Eio);
+	}
+	if(i)
+		WR(Interrupt, i);
+	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 = {
+	"emmc",
+	emmcinit,
+	emmcenable,
+	emmcinquiry,
+	emmccmd,
+	emmciosetup,
+	emmcio,
+	.highspeed = 1,
+};
--- /dev/null
+++ b/v1/9/emmc/port/sdmmc.c
@@ -1,0 +1,444 @@
+/*
+ * mmc / sd memory card
+ *
+ * Copyright © 2012 Richard Miller <[email protected]>
+ *
+ * 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,
+	MMC_TYPE_SD = 1,
+	MMC_TYPE_MMC = 2,
+
+	/* Commands */
+	GO_IDLE_STATE	= 0,
+	ALL_SEND_CID	= 2,
+	SEND_RELATIVE_ADDR= 3,
+	SWITCH_FUNC	= 6,
+	SELECT_CARD	= 7,
+	SD_SEND_IF_COND	= 8,
+	SEND_EXT_CSD = 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,
+	MMC_SEND_OP_COND = 1,
+
+	/* 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,
+
+	/* SWITCH_FUNC */
+	Dfltspeed	= 0<<0,
+	Hispeed		= 1<<0,
+	Checkfunc	= 0x00FFFFF0,
+	Setfunc		= 0x80FFFFF0,
+	Funcbytes	= 64,
+
+	/* 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];
+
+	int	retry;
+
+	uchar type;
+};
+
+SDio *sdcardlink;
+
+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)
+{
+	uint csize, mult;
+	Ctlr *ctl = unit->dev->ctlr;
+	u32int *csd = ctl->csd;
+	SDio *io = ctl->io;                                                     
+	uchar ext_csd[512];
+	u32int r[4];
+
+	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) * 0x80000LL / unit->secsize;
+		break;
+	case 3:
+		if(CSD(125, 122) < 4) { /* no EXT_CSD */
+			print("sdmmc: no ext_csd\n");                           
+			break;
+		}
+		if(waserror()){
+			print("sdmmc: send_ext_csd error\n");
+			nexterror();
+		}
+		io->iosetup(0, ext_csd, 512, 1);
+		io->cmd(SEND_EXT_CSD, 0, r);
+		io->io(0, ext_csd, 512);
+		unit->sectors = ext_csd[212] | (ext_csd[213]<<8) |
+					(ext_csd[214]<<16) | (ext_csd[215]<<24);
+		poperror();
+		break;
+	}
+	if(unit->secsize == 1024){
+		unit->sectors <<= 1;
+		unit->secsize = 512;
+	}
+}
+
+static SDev*
+mmcpnp(void)
+{
+	SDev *sdev;
+	Ctlr *ctl;
+
+	if(sdcardlink == nil)
+		sdcardlink = &sdio;
+	if(sdcardlink->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 = sdcardlink;
+	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] = 0x00;	/* direct access (disk) */
+	unit->inquiry[1] = 0x80;	/* removable */
+	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 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
+cardinit(Ctlr *ctl)
+{
+	u32int r[4];
+	int hcs, i;
+	SDio *io = ctl->io;
+
+	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();
+	}
+	if(!waserror()){
+		for(i = 0; i < Inittimeout; i++){
+			tsleep(&up->sleep, return0, nil, 100);
+			io->cmd(APP_CMD, 0, r);
+			io->cmd(SD_SEND_OP_COND, hcs|V3_3, r);
+			if(r[0] & Powerup){
+				ctl->type = MMC_TYPE_SD;
+				break;
+			}
+		}
+	}else{
+		io->cmd(GO_IDLE_STATE, 0, r);
+		ctl->ocr = (1 << 30);
+		for(i = 0; i < Inittimeout; i++){
+			tsleep(&up->sleep, return0, nil, 100);
+			io->cmd(MMC_SEND_OP_COND, ctl->ocr, r);
+			if(r[0] & Powerup){
+				ctl->type = MMC_TYPE_MMC;
+				break;
+			}
+			ctl->ocr = r[0] | (1 << 30);
+		}
+	}
+	if(ctl->type == 0)
+		return 3;
+	if(i == Inittimeout)
+		return 2;
+	ctl->ocr = r[0];
+	io->cmd(ALL_SEND_CID, 0, r);
+	memmove(ctl->cid, r, sizeof ctl->cid);
+	if(ctl->type == MMC_TYPE_SD){
+		io->cmd(SEND_RELATIVE_ADDR, 0, r);
+		ctl->rca = r[0]>>16;
+	}else if(ctl->type == MMC_TYPE_MMC){
+		ctl->rca = 1;
+		io->cmd(SEND_RELATIVE_ADDR, ctl->rca<<Rcashift, r);
+	}
+	io->cmd(SEND_CSD, ctl->rca<<Rcashift, r);
+	memmove(ctl->csd, r, sizeof ctl->csd);
+	return 1;
+}
+
+static void
+retryproc(void *arg)
+{
+	Ctlr *ctl = arg;
+	int i = 0;
+
+	while(waserror())
+		;
+	if(i++ < ctl->retry)
+		cardinit(ctl);
+	USED(i);
+	ctl->retry = 0;
+	pexit("", 1);
+}
+
+static int
+mmconline(SDunit *unit)
+{
+	u32int r[4];
+	Ctlr *ctl;
+	SDio *io;
+
+	assert(unit->subno == 0);
+	ctl = unit->dev->ctlr;
+	io = ctl->io;
+
+	if(ctl->retry)
+		return 0;
+	if(waserror()){
+		unit->sectors = 0;
+		if(ctl->retry++ == 0)
+			kproc(unit->name, retryproc, ctl);
+		return 0;
+	}
+	if(unit->sectors != 0){
+		io->cmd(SEND_STATUS, ctl->rca<<Rcashift, r);
+		poperror();
+		return 1;
+	}
+	if(cardinit(ctl) != 1){
+		poperror();
+		return 2;
+	}
+	io->cmd(SELECT_CARD, ctl->rca<<Rcashift, r);
+	identify(unit);
+	io->cmd(SET_BLOCKLEN, unit->secsize, r);
+	if(ctl->type == MMC_TYPE_SD){
+		io->cmd(APP_CMD, ctl->rca<<Rcashift, r);
+		io->cmd(SET_BUS_WIDTH, Width4, r);
+		if(io->highspeed){
+			if(!waserror()){
+				mmcswitchfunc(io, Hispeed|Setfunc);
+				poperror();
+			}
+		}
+	}
+	poperror();
+	return 1;
+}
+
+static int
+mmcrctl(SDunit *unit, char *p, int l)
+{
+	Ctlr *ctl;
+	int i, n;
+
+	assert(unit->subno == 0);
+	if(unit->sectors == 0){
+		mmconline(unit);
+		if(unit->sectors == 0)
+			return 0;
+	}
+	ctl = unit->dev->ctlr;
+	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(Echange);
+	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 *r)
+{
+	int i, rw, count;
+	uvlong lba;
+
+	if((i = sdfakescsi(r)) != SDnostatus)
+		return r->status = i;
+	if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
+		return i;
+	r->rlen = mmcbio(r->unit, r->lun, rw == SDwrite, r->data, count, lba);
+	return r->status = SDok;
+}
+
+SDifc sdmmcifc = {
+	.name	= "mmc",
+	.pnp	= mmcpnp,
+	.enable	= mmcenable,
+	.verify	= mmcverify,
+	.online	= mmconline,
+	.rctl	= mmcrctl,
+	.bio	= mmcbio,
+	.rio	= mmcrio,
+};
--- /dev/null
+++ b/v1/9/i2c/bcm/devi2c.c
@@ -1,0 +1,227 @@
+/*
+ * i2c
+ *
+ * Copyright © 1998, 2003 Vita Nuova Limited.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+typedef struct I2Cdir I2Cdir;
+
+enum{
+	Qdir,
+	Qdata,
+	Qctl,
+};
+
+static
+Dirtab i2ctab[]={
+	".",	{Qdir, 0, QTDIR},	0,	0555,
+	"i2cdata",		{Qdata, 0},	256,	0660,
+	"i2cctl",		{Qctl, 0},		0,	0660,
+};
+
+struct I2Cdir {
+	Ref;
+	I2Cdev;
+	Dirtab	tab[nelem(i2ctab)];
+};
+
+static void
+i2creset(void)
+{
+	i2csetup(0);
+}
+
+static Chan*
+i2cattach(char* spec)
+{
+	char *s;
+	ulong addr;
+	I2Cdir *d;
+	Chan *c;
+
+	addr = strtoul(spec, &s, 16);
+	if(*spec == 0 || *s || addr >= (1<<10))
+		error("invalid i2c address");
+	d = malloc(sizeof(I2Cdir));
+	if(d == nil)
+		error(Enomem);
+	d->ref = 1;
+	d->addr = addr;
+	d->salen = 0;
+	d->tenbit = addr >= 128;
+	memmove(d->tab, i2ctab, sizeof(d->tab));
+	sprint(d->tab[1].name, "i2c.%lux.data", addr);
+	sprint(d->tab[2].name, "i2c.%lux.ctl", addr);
+
+	c = devattach('J', spec);
+	c->aux = d;
+	return c;
+}
+
+static Walkqid*
+i2cwalk(Chan* c, Chan *nc, char **name, int nname)
+{
+	Walkqid *wq;
+	I2Cdir *d;
+
+	d = c->aux;
+	wq = devwalk(c, nc, name, nname, d->tab, nelem(d->tab), devgen);
+	if(wq != nil && wq->clone != nil && wq->clone != c)
+		incref(d);
+	return wq;
+}
+
+static int
+i2cstat(Chan* c, uchar *dp, int n)
+{
+	I2Cdir *d;
+
+	d = c->aux;
+	return devstat(c, dp, n, d->tab, nelem(d->tab), devgen);
+}
+
+static Chan*
+i2copen(Chan* c, int omode)
+{
+	I2Cdir *d;
+
+	d = c->aux;
+	return devopen(c, omode, d->tab, nelem(d->tab), devgen);
+}
+
+static void
+i2cclose(Chan *c)
+{
+	I2Cdir *d;
+
+	d = c->aux;
+	if(decref(d) == 0)
+		free(d);
+}
+
+static long
+i2cread(Chan *c, void *a, long n, vlong offset)
+{
+	I2Cdir *d;
+	char *s, *e;
+	ulong len;
+
+	d = c->aux;
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, d->tab, nelem(d->tab), devgen);
+	case Qdata:
+		len = d->tab[1].length;
+		if(offset+n >= len){
+			n = len - offset;
+			if(n <= 0)
+				return 0;
+		}
+		n = i2crecv(d, a, n, offset);
+		break;
+	case Qctl:
+		s = smalloc(READSTR);
+		if(waserror()){
+			free(s);
+			nexterror();
+		}
+		e = seprint(s, s+READSTR, "size %lud\n", (ulong)d->tab[1].length);
+		if(d->salen)
+			e = seprint(e, s+READSTR, "subaddress %d\n", d->salen);
+		if(d->tenbit)
+			seprint(e, s+READSTR, "a10\n");
+		n = readstr(offset, a, n, s);
+		poperror();
+		free(s);
+		return n;
+	default:
+		n=0;
+		break;
+	}
+	return n;
+}
+
+static long
+i2cwrite(Chan *c, void *a, long n, vlong offset)
+{
+	I2Cdir *d;
+	long len;
+	Cmdbuf *cb;
+
+	USED(offset);
+	switch((ulong)c->qid.path){
+	case Qdata:
+		d = c->aux;
+		len = d->tab[1].length;
+		if(offset+n >= len){
+			n = len - offset;
+			if(n <= 0)
+				return 0;
+		}
+		n = i2csend(d, a, n, offset);
+		break;
+	case Qctl:
+		cb = parsecmd(a, n);
+		if(waserror()){
+			free(cb);
+			nexterror();
+		}
+		if(cb->nf < 1)
+			error(Ebadctl);
+		d = c->aux;
+		if(strcmp(cb->f[0], "subaddress") == 0){
+			if(cb->nf > 1){
+				len = strtol(cb->f[1], nil, 0);
+				if(len <= 0)
+					len = 0;
+				if(len > 4)
+					cmderror(cb, "subaddress too long");
+			}else
+				len = 1;
+			d->salen = len;
+		}else if(cb->nf > 1 && strcmp(cb->f[0], "size") == 0){
+			len = strtol(cb->f[1], nil, 0);
+			if(len < 0)
+				cmderror(cb, "size is negative");
+			d->tab[1].length = len;
+		}else if(strcmp(cb->f[0], "a10") == 0)
+			d->tenbit = 1;
+		else
+			cmderror(cb, "unknown control request");
+		poperror();
+		free(cb);
+		break;
+	default:
+		error(Ebadusefd);
+	}
+	return n;
+}
+
+Dev i2cdevtab = {
+	'J',
+	"i2c",
+
+	i2creset,
+	devinit,
+	devshutdown,
+	i2cattach,
+	i2cwalk,
+	i2cstat,
+	i2copen,
+	devcreate,
+	i2cclose,
+	i2cread,
+	devbread,
+	i2cwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--- /dev/null
+++ b/v1/9/i2c/bcm/devrtc7940x.c
@@ -1,0 +1,378 @@
+/*
+ * MCP7940x realtime clock (accessed via rtc)
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+enum {
+	/* registers */
+	Seconds=	0,
+	Minutes=	1,
+	Hours=		2,
+	Weekday=	3,
+	Mday=		4,
+	Month=		5,
+	Year=		6,
+	Nbcd=		7,
+
+	/* Hours register may be in 12-hour or 24-hour mode */
+	Twelvehr=	0x40,
+	Pm=			0x20,
+
+	Start=		0x80,
+	Vbaten=		0x08,
+
+	I2Caddr=	0x6F,
+	
+};
+
+typedef struct Rtc	Rtc;
+
+struct Rtc
+{
+	int	sec;
+	int	min;
+	int	hour;
+	int	mday;
+	int	mon;
+	int	year;
+};
+
+enum{
+	Qdir = 0,
+	Qrtc,
+};
+
+Dirtab rtcdir[]={
+	".",	{Qdir, 0, QTDIR},	0,	0555,
+	"rtc",		{Qrtc, 0},	0,	0664,
+};
+
+static ulong rtc2sec(Rtc*);
+static void sec2rtc(ulong, Rtc*);
+
+static void
+i2cread(uint addr, void *buf, int len)
+{
+	I2Cdev d;
+
+	d.addr = addr;
+	d.tenbit = 0;
+	d.salen = 1;
+	i2crecv(&d, buf, len, 0);
+}
+
+static void
+i2cwrite(uint addr, void *buf, int len)
+{
+	I2Cdev d;
+
+	d.addr = addr;
+	d.tenbit = 0;
+	d.salen = 1;
+	i2csend(&d, buf, len, 0);
+}
+
+static void
+rtcinit()
+{
+	i2csetup(0);
+}
+
+static Chan*
+rtcattach(char* spec)
+{
+	return devattach('r', spec);
+}
+
+static Walkqid*	 
+rtcwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
+}
+
+static int	 
+rtcstat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
+}
+
+static Chan*
+rtcopen(Chan* c, int omode)
+{
+	char dummy;
+
+	omode = openmode(omode);
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		if(strcmp(up->user, eve)!=0 && omode!=OREAD)
+			error(Eperm);
+		/* if it's not there, this will throw an error */
+		i2cread(I2Caddr, &dummy, 1);
+		break;
+	}
+	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
+}
+
+static void	 
+rtcclose(Chan*)
+{
+}
+
+static int
+bcd(int n)
+{
+	return (n & 0xF) + (10 * (n >> 4));
+}
+
+long	 
+rtctime(void)
+{
+	uchar clk[Nbcd];
+	Rtc rtc;
+
+	clk[0] = 0;
+	i2cwrite(I2Caddr, clk, 1);
+	i2cread(I2Caddr, clk, Nbcd);
+
+	/*
+	 *  convert from BCD
+	 */
+	rtc.sec = bcd(clk[Seconds] & 0x7F);
+	rtc.min = bcd(clk[Minutes] & 0x7F);
+	rtc.hour = bcd(clk[Hours] & 0x3F);
+	if(clk[Hours] & Twelvehr){
+		rtc.hour = bcd(clk[Hours] & 0x1F);
+		if(clk[Hours] & Pm)
+			rtc.hour += 12;
+	}
+	rtc.mday = bcd(clk[Mday] & 0x3F);
+	rtc.mon = bcd(clk[Month] & 0x1F);
+	rtc.year = bcd(clk[Year]);
+
+	/*
+	 *  the world starts jan 1 1970
+	 */
+	if(rtc.year < 70)
+		rtc.year += 2000;
+	else
+		rtc.year += 1900;
+	return rtc2sec(&rtc);
+}
+
+
+static long	 
+rtcread(Chan* c, void* buf, long n, vlong off)
+{
+	ulong t;
+	ulong offset = off;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		t = rtctime();
+		n = readnum(offset, buf, n, t, 12);
+		return n;
+	}
+	error(Ebadarg);
+	return 0;
+}
+
+#define PUTBCD(n,o) bcdclock[1+o] = (n % 10) | (((n / 10) % 10)<<4)
+
+static long	 
+rtcwrite(Chan* c, void* buf, long n, vlong off)
+{
+	Rtc rtc;
+	ulong secs;
+	uchar bcdclock[1+Nbcd];
+	char *cp, *ep;
+	ulong offset = off;
+
+	if(offset!=0)
+		error(Ebadarg);
+
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		/*
+		 *  read the time
+		 */
+		cp = ep = buf;
+		ep += n;
+		while(cp < ep){
+			if(*cp>='0' && *cp<='9')
+				break;
+			cp++;
+		}
+		secs = strtoul(cp, 0, 0);
+	
+		/*
+		 *  convert to bcd
+		 */
+		sec2rtc(secs, &rtc);
+		PUTBCD(rtc.sec, Seconds);
+		PUTBCD(rtc.min, Minutes);	/* forces 24 hour mode */
+		PUTBCD(rtc.hour, Hours);
+		PUTBCD(0, Weekday);		/* hope no other OS uses this */
+		PUTBCD(rtc.mday, Mday);
+		PUTBCD(rtc.mon, Month);
+		PUTBCD(rtc.year, Year);
+
+		/*
+		 *  write the clock
+		 */
+		bcdclock[0] = 0;
+
+		bcdclock[1+Seconds] |= Start;
+		bcdclock[1+Weekday] |= Vbaten;
+
+		i2cwrite(I2Caddr, bcdclock, 1+Nbcd);
+		return n;
+	}
+	error(Ebadarg);
+	return 0;
+}
+
+Dev rtc7940xdevtab = {
+	'r',
+	"rtc",
+
+	devreset,
+	rtcinit,
+	devshutdown,
+	rtcattach,
+	rtcwalk,
+	rtcstat,
+	rtcopen,
+	devcreate,
+	rtcclose,
+	rtcread,
+	devbread,
+	rtcwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ *  days per month plus days/year
+ */
+static	int	dmsize[] =
+{
+	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static	int	ldmsize[] =
+{
+	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ *  return the days/month for the given year
+ */
+static int*
+yrsize(int y)
+{
+	if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
+		return ldmsize;
+	else
+		return dmsize;
+}
+
+/*
+ *  compute seconds since Jan 1 1970
+ */
+static ulong
+rtc2sec(Rtc *rtc)
+{
+	ulong secs;
+	int i;
+	int *d2m;
+
+	secs = 0;
+
+	/*
+	 *  seconds per year
+	 */
+	for(i = 1970; i < rtc->year; i++){
+		d2m = yrsize(i);
+		secs += d2m[0] * SEC2DAY;
+	}
+
+	/*
+	 *  seconds per month
+	 */
+	d2m = yrsize(rtc->year);
+	for(i = 1; i < rtc->mon; i++)
+		secs += d2m[i] * SEC2DAY;
+
+	secs += (rtc->mday-1) * SEC2DAY;
+	secs += rtc->hour * SEC2HOUR;
+	secs += rtc->min * SEC2MIN;
+	secs += rtc->sec;
+
+	return secs;
+}
+
+/*
+ *  compute rtc from seconds since Jan 1 1970
+ */
+static void
+sec2rtc(ulong secs, Rtc *rtc)
+{
+	int d;
+	long hms, day;
+	int *d2m;
+
+	/*
+	 * break initial number into days
+	 */
+	hms = secs % SEC2DAY;
+	day = secs / SEC2DAY;
+	if(hms < 0) {
+		hms += SEC2DAY;
+		day -= 1;
+	}
+
+	/*
+	 * generate hours:minutes:seconds
+	 */
+	rtc->sec = hms % 60;
+	d = hms / 60;
+	rtc->min = d % 60;
+	d /= 60;
+	rtc->hour = d;
+
+	/*
+	 * year number
+	 */
+	if(day >= 0)
+		for(d = 1970; day >= *yrsize(d); d++)
+			day -= *yrsize(d);
+	else
+		for (d = 1970; day < 0; d--)
+			day += *yrsize(d-1);
+	rtc->year = d;
+
+	/*
+	 * generate month
+	 */
+	d2m = yrsize(rtc->year);
+	for(d = 1; day >= d2m[d]; d++)
+		day -= d2m[d];
+	rtc->mday = day + 1;
+	rtc->mon = d;
+
+	return;
+}
--- /dev/null
+++ b/v1/9/i2c/bcm/i2c.c
@@ -1,0 +1,240 @@
+/*
+ * bcm2835 i2c controller
+ *
+ *	Only i2c1 is supported.
+ *	i2c2 is reserved for HDMI.
+ *	i2c0 SDA0/SCL0 pins are not routed to P1 connector (except for early Rev 0 boards)
+ *
+ * maybe hardware problems lurking, see: https://github.com/raspberrypi/linux/issues/254
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"../port/error.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#define I2CREGS	(VIRTIO+0x205000)
+#define SDA0Pin	44
+#define	SCL0Pin 45
+
+typedef struct I2c I2c;
+typedef struct Bsc Bsc;
+
+/*
+ * Registers for Broadcom Serial Controller (i2c compatible)
+ */
+struct Bsc {
+	u32int	ctrl;
+	u32int	stat;
+	u32int	dlen;
+	u32int	addr;
+	u32int	fifo;
+	u32int	clkdiv;		/* default 1500 => 100 KHz assuming 150Mhz input clock */
+	u32int	delay;		/* default (48<<16)|48 falling:rising edge */
+	u32int	clktimeout;	/* default 64 */
+};
+
+/*
+ * Per-controller info
+ */
+struct I2c {
+	QLock	lock;
+	Lock	reglock;
+	Rendez	r;
+	Bsc	*regs;
+};
+
+static I2c i2c;
+
+enum {
+	/* ctrl */
+	I2cen	= 1<<15,	/* I2c enable */
+	Intr	= 1<<10,	/* interrupt on reception */
+	Intt	= 1<<9,		/* interrupt on transmission */
+	Intd	= 1<<8,		/* interrupt on done */
+	Start	= 1<<7,		/* aka ST, start a transfer */
+	Clear	= 1<<5,		/* clear fifo */
+	Read	= 1<<0,		/* read transfer */
+	Write	= 0<<0,		/* write transfer */
+
+	/* stat */
+	Clkt	= 1<<9,		/* clock stretch timeout */
+	Err	= 1<<8,			/* NAK */
+	Rxf	= 1<<7,			/* RX fifo full */
+	Txe	= 1<<6,			/* TX fifo full */
+	Rxd	= 1<<5,			/* RX fifo has data */
+	Txd	= 1<<4,			/* TX fifo has space */
+	Rxr	= 1<<3,			/* RX fiio needs reading */
+	Txw	= 1<<2,			/* TX fifo needs writing */
+	Done	= 1<<1,		/* transfer done */
+	Ta	= 1<<0,			/* Transfer active */
+
+	/* maximum I2C I/O (can change) */
+	MaxIO =	128,
+	MaxSA =	2,	/* longest subaddress */
+	Bufsize = (MaxIO+MaxSA+1+4)&~3,		/* extra space for subaddress/clock bytes and alignment */
+
+	Chatty = 0,
+};
+
+static void
+i2cinterrupt(Ureg*, void*)
+{
+	Bsc *r;
+	int st;
+
+	r = i2c.regs;
+	st = 0;
+	if((r->ctrl & Intr) && (r->stat & Rxd))
+		st |= Intr;
+	if((r->ctrl & Intt) && (r->stat & Txd))
+		st |= Intt;
+	if(r->stat & Done)
+		st |= Intd;
+	if(st){
+		r->ctrl &= ~st;
+		wakeup(&i2c.r);
+	}
+}
+
+static int
+i2cready(void *st)
+{
+	return (i2c.regs->stat & (uintptr)st);
+}
+
+static void
+i2cinit(void)
+{
+	i2c.regs = (Bsc*)I2CREGS;
+	i2c.regs->clkdiv = 2500;
+
+	gpiosel(SDA0Pin, Alt1);
+	gpiosel(SCL0Pin, Alt1);
+	gpiopullup(SDA0Pin);
+	gpiopullup(SCL0Pin);
+
+	intrenable(IRQi2c, i2cinterrupt, 0, 0, "i2c");
+}
+
+/*
+ * To do subaddressing avoiding a STOP condition between the address and payload.
+ * 	- write the subaddress,
+ *	- poll until the transfer starts,
+ *	- overwrite the registers for the payload transfer, before the subaddress
+ * 		transaction has completed.
+ *
+ * FIXME: neither 10bit adressing nor 100Khz transfers implemented yet.
+ */
+static void
+i2cio(int rw, int tenbit, uint addr, void *buf, int len, int salen, uint subaddr)
+{
+	Bsc *r;
+	uchar *p;
+	int st;
+	char errbuf[128];
+
+	if(tenbit)
+		error("10bit addressing not supported");
+	if(salen == 0 && subaddr)	/* default subaddress len == 1byte */
+		salen = 1;
+
+	qlock(&i2c.lock);
+	r = i2c.regs;
+	r->ctrl = Clear;
+	r->addr = addr;
+	r->stat = Clkt|Err|Done;
+
+	if(salen){
+		r->dlen = salen;
+		r->ctrl = I2cen | Start | Write;
+		while((r->stat & Ta) == 0) {
+			if(r->stat & (Err|Clkt)) {
+				qunlock(&i2c.lock);
+				snprint(errbuf, 127, "%s 1 %02x %d %s %s %s", Eio, subaddr, len, (r->stat & Err)? "Err": "", (r->stat & Clkt)? "Clkt": "", (r->stat & Done)? "Done": "");
+				error(errbuf);
+			}
+		}
+		r->dlen = len;
+		r->ctrl = I2cen | Start | Intd | rw;
+		for(; salen > 0; salen--)
+			r->fifo = subaddr >> ((salen-1)*8);
+		/*
+		 * Adapted from Linux code...uses undocumented
+		 * status information.
+		 */
+		if(rw == Read) {
+			do {
+				if(r->stat & (Err|Clkt)) {
+					qunlock(&i2c.lock);
+					snprint(errbuf, 127, "%s 2 %02x %d %s %s %s", Eio, subaddr, len, (r->stat & Err)? "Err": "", (r->stat & Clkt)? "Clkt": "", (r->stat & Done)? "Done": "");
+					error(errbuf);
+				}
+				st = r->stat >> 28;
+			} while(st != 0 && st != 4 && st != 5);
+		}
+	}
+	else {
+		r->dlen = len;
+		r->ctrl = I2cen | Start | Intd | rw;
+	}
+
+	p = buf;
+	st = rw == Read? Rxd : Txd;
+	while(len > 0){
+		while((r->stat & (st|Done)) == 0){
+			r->ctrl |= rw == Read? Intr : Intt;
+			sleep(&i2c.r, i2cready, (void*)(st|Done));
+		}
+		if(r->stat & (Err|Clkt)){
+			qunlock(&i2c.lock);
+			snprint(errbuf, 127, "%s 3 %02x %d %s %s %s", Eio, subaddr, len, (r->stat & Err)? "Err": "", (r->stat & Clkt)? "Clkt": "", (r->stat & Done)? "Done": "");
+			error(errbuf);
+		}
+		if(rw == Read){
+			do{
+				*p++ = r->fifo;
+				len--;
+			}while ((r->stat & Rxd) && len > 0);
+		}else{
+			do{
+				r->fifo = *p++;
+				len--;
+			}while((r->stat & Txd) && len > 0);
+		}
+	}
+	while((r->stat & Done) == 0)
+		sleep(&i2c.r, i2cready, (void*)Done);
+	if(r->stat & (Err|Clkt)){
+		qunlock(&i2c.lock);
+		snprint(errbuf, 127, "%s 4 %02x %d %s %s %s", Eio, subaddr, len, (r->stat & Err)? "Err": "", (r->stat & Clkt)? "Clkt": "", (r->stat & Done)? "Done": "");
+		error(errbuf);
+	}
+	r->ctrl = 0;
+	qunlock(&i2c.lock);
+}
+
+
+void
+i2csetup(int)
+{
+	//print("i2csetup\n");
+	i2cinit();
+}
+
+long
+i2csend(I2Cdev *d, void *buf, long len, ulong offset)
+{
+	i2cio(Write, d->tenbit, d->addr, buf, len, d->salen, offset);
+	return len;
+}
+
+long
+i2crecv(I2Cdev *d, void *buf, long len, ulong offset)
+{
+	i2cio(Read, d->tenbit, d->addr, buf, len, d->salen, offset);
+	return len;
+}
--- /dev/null
+++ b/v1/9/i2c/bcm64/dat.h
@@ -1,0 +1,254 @@
+/*
+ * Time.
+ *
+ * HZ should divide 1000 evenly, ideally.
+ * 100, 125, 200, 250 and 333 are okay.
+ */
+#define	HZ		100			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+
+enum {
+	Mhz	= 1000 * 1000,
+};
+
+typedef struct Conf	Conf;
+typedef struct Confmem	Confmem;
+typedef struct FPsave	FPsave;
+typedef struct I2Cdev	I2Cdev;
+typedef struct PFPU	PFPU;
+typedef struct ISAConf	ISAConf;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Memcache	Memcache;
+typedef struct MMMU	MMMU;
+typedef struct Mach	Mach;
+typedef struct Page	Page;
+typedef struct PhysUart	PhysUart;
+typedef struct Pcidev	Pcidev;
+typedef struct PMMU	PMMU;
+typedef struct Proc	Proc;
+typedef u64int		PTE;
+typedef struct Soc	Soc;
+typedef struct Uart	Uart;
+typedef struct Ureg	Ureg;
+typedef uvlong		Tval;
+typedef void		KMap;
+
+
+#pragma incomplete Pcidev
+#pragma incomplete Ureg
+
+#define MAXSYSARG	5	/* for mount(fd, mpt, flag, arg, srv) */
+
+/*
+ *  parameters for sysproc.c
+ */
+#define AOUT_MAGIC	(R_MAGIC)
+
+struct Lock
+{
+	ulong	key;
+	u32int	sr;
+	uintptr	pc;
+	Proc*	p;
+	Mach*	m;
+	int	isilock;
+};
+
+struct Label
+{
+	uintptr	sp;
+	uintptr	pc;
+};
+
+struct FPsave
+{
+	uvlong	regs[32][2];
+
+	ulong	control;
+	ulong	status;
+};
+
+struct PFPU
+{
+	FPsave	fpsave[1];
+
+	int	fpstate;
+};
+
+enum
+{
+	FPinit,
+	FPactive,
+	FPinactive,
+
+	/* bits or'd with the state */
+	FPillegal= 0x100,
+};
+
+struct Confmem
+{
+	uintptr	base;
+	ulong	npage;
+	uintptr	limit;
+	uintptr	kbase;
+	uintptr	klimit;
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	Confmem	mem[4];		/* physical memory */
+	ulong	npage;		/* total physical pages of memory */
+	ulong	upages;		/* user page pool */
+	ulong	copymode;	/* 0 is copy on write, 1 is copy on reference */
+	ulong	ialloc;		/* max interrupt time allocation in bytes */
+	ulong	pipeqsize;	/* size in bytes of pipe queues */
+	ulong	nimage;		/* number of page cache image headers */
+	ulong	nswap;		/* number of swap pages */
+	int	nswppo;		/* max # of pageouts per segment pass */
+	ulong	hz;		/* processor cycle freq */
+	ulong	mhz;
+	int	monitor;	/* flag */
+};
+
+struct I2Cdev {
+	int	salen;
+	int	addr;
+	int	tenbit;
+};
+
+/*
+ *  MMU stuff in Mach.
+ */
+struct MMMU
+{
+	PTE*	mmutop;		/* first level user page table */
+};
+
+/*
+ *  MMU stuff in proc
+ */
+#define NCOLOR	1		/* 1 level cache, don't worry about VCE's */
+
+struct PMMU
+{
+	union {
+	Page	*mmufree;	/* mmuhead[0] is freelist head */
+	Page	*mmuhead[PTLEVELS];
+	};
+	Page	*mmutail[PTLEVELS];
+	int	asid;
+	uintptr	tpidr;
+};
+
+#include "../port/portdat.h"
+
+struct Mach
+{
+	int	machno;			/* physical id of processor */
+	uintptr	splpc;			/* pc of last caller to splhi */
+	Proc*	proc;			/* current process on this processor */
+	/* end of offsets known to asm */
+
+	MMMU;
+
+	PMach;
+
+	int	cputype;
+	ulong	delayloop;
+	int	cpumhz;
+	uvlong	cpuhz;			/* speed of cpu */
+
+	int	stack[1];
+};
+
+struct
+{
+	char	machs[MAXMACH];		/* active CPUs */
+	int	exiting;		/* shutdown */
+}active;
+
+#define MACHP(n)	((Mach*)MACHADDR(n))
+
+extern register Mach* m;			/* R27 */
+extern register Proc* up;			/* R26 */
+extern int normalprint;
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define NISAOPT		8
+
+struct ISAConf {
+	char	*type;
+	uvlong	port;
+	int	irq;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	*opt[NISAOPT];
+};
+
+/*
+ * Horrid. But the alternative is 'defined'.
+ */
+#ifdef _DBGC_
+#define DBGFLG		(dbgflg[_DBGC_])
+#else
+#define DBGFLG		(0)
+#endif /* _DBGC_ */
+
+int vflag;
+extern char dbgflg[256];
+
+#define dbgprint	print		/* for now */
+
+/*
+ *  hardware info about a device
+ */
+typedef struct {
+	ulong	port;
+	int	size;
+} Devport;
+
+struct DevConf
+{
+	ulong	intnum;			/* interrupt number */
+	char	*type;			/* card type, malloced */
+	int	nports;			/* Number of ports */
+	Devport	*ports;			/* The ports themselves */
+};
+
+struct Soc {			/* SoC dependent configuration */
+	ulong	dramsize;
+	uintptr	busdram;
+	ulong	iosize;
+	uintptr	busio;
+	uintptr	physio;
+	uintptr	virtio;
+	uintptr	armlocal;
+	uintptr	pciwin;		/* PCI outbound window CPU->PCI */
+	uintptr	pcidmawin;	/* PCI inbound window PCI->DRAM */
+	int	oscfreq;
+};
+extern Soc soc;
+
+/*
+ * GPIO
+ */
+enum {
+	Input	= 0x0,
+	Output	= 0x1,
+	Alt0	= 0x4,
+	Alt1	= 0x5,
+	Alt2	= 0x6,
+	Alt3	= 0x7,
+	Alt4	= 0x3,
+	Alt5	= 0x2,
+};
--- /dev/null
+++ b/v1/9/i2c/bcm64/fns.h
@@ -1,0 +1,195 @@
+#include "../port/portfns.h"
+
+/* l.s */
+extern void sev(void);
+extern int tas(void *);
+extern int cmpswap(long*, long, long);
+extern void coherence(void);
+extern void idlehands(void);
+extern uvlong vcycles(void);
+#define cycles(ip) *(ip) = vcycles()
+extern int splfhi(void);
+extern void splflo(void);
+extern void touser(uintptr sp);
+extern void forkret(void);
+extern void noteret(void);
+extern void returnto(void*);
+extern void fpsaveregs(void*);
+extern void fploadregs(void*);
+
+extern void setttbr(uintptr pa);
+extern uintptr getfar(void);
+
+extern void flushasidva(uintptr asidva);
+extern void tlbivae1is(uintptr asidva);
+
+extern void flushasidvall(uintptr asidva);
+extern void tlbivale1is(uintptr asidva);
+
+extern void flushasid(uintptr asid);
+extern void tlbiaside1is(uintptr asid);
+
+extern void flushtlb(void);
+extern void tlbivmalle1(void);
+
+extern void flushlocaltlb(void);
+extern void tlbivmalle1(void);
+
+/* cache */
+extern ulong cachesize(int level);
+
+extern void cacheiinvse(void*, int);
+extern void cacheuwbinv(void);
+extern void cacheiinv(void);
+
+extern void cachedwbse(void*, int);
+extern void cacheduwbse(void*, int);
+extern void cachedinvse(void*, int);
+extern void cachedwbinvse(void*, int);
+
+extern void cachedwb(void);
+extern void cachedinv(void);
+extern void cachedwbinv(void);
+
+extern void l2cacheuwb(void);
+extern void l2cacheuinv(void);
+extern void l2cacheuwbinv(void);
+
+/* mmu */
+#define	getpgcolor(a)	0
+extern uintptr paddr(void*);
+#define PADDR(a) paddr((void*)(a))
+extern uintptr cankaddr(uintptr);
+extern void* kaddr(uintptr);
+#define KADDR(a) kaddr(a)
+extern void kmapinval(void);
+#define	VA(k)	((uintptr)(k))
+extern KMap *kmap(Page*);
+extern void kunmap(KMap*);
+extern uintptr mmukmap(uintptr, uintptr, usize);
+extern void* vmap(uvlong, vlong);
+extern void vunmap(void*, vlong);
+
+extern void mmu0init(uintptr*);
+extern void mmu0clear(uintptr*);
+extern void mmuidmap(uintptr*);
+extern void mmu1init(void);
+extern void meminit(void);
+
+extern void putasid(Proc*);
+
+/* clock */
+extern void clockinit(void);
+extern void synccycles(void);
+extern void armtimerset(int);
+extern void clockshutdown(void);
+
+/* fpu */
+extern void fpuinit(void);
+extern void fpoff(void);
+extern void fpinit(void);
+extern void fpclear(void);
+extern void fpsave(FPsave*);
+extern void fprestore(FPsave*);
+extern void mathtrap(Ureg*);
+
+/* trap */
+extern void trapinit(void);
+extern int userureg(Ureg*);
+extern void evenaddr(uintptr);
+extern void setkernur(Ureg*, Proc*);
+extern void procfork(Proc*);
+extern void procsetup(Proc*);
+extern void procsave(Proc*);
+extern void procrestore(Proc *);
+extern void trap(Ureg*);
+extern void syscall(Ureg*);
+extern void noted(Ureg*, ulong);
+extern void faultarm64(Ureg*);
+extern void dumpstack(void);
+extern void dumpregs(Ureg*);
+
+/* irq */
+extern void intrcpushutdown(void);
+extern void intrsoff(void);
+extern void intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+extern void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
+extern int irq(Ureg*);
+extern void fiq(Ureg*);
+
+/* sysreg */
+extern uvlong	sysrd(ulong);
+extern void	syswr(ulong, uvlong);
+
+/* gpio */
+extern void gpiosel(uint, int);
+extern void gpiopull(uint, int);
+extern void gpiopullup(uint);
+extern void gpiopulloff(uint);
+extern void gpiopulldown(uint);
+extern void gpioout(uint, int);
+extern int gpioin(uint);
+extern void gpioselevent(uint, int, int);
+extern int gpiogetevent(uint);
+extern void gpiomeminit(void);
+
+/* i2c */
+extern void i2csetup(int);
+extern long i2crecv(I2Cdev*, void*, long, ulong);
+extern long i2csend(I2Cdev*, void*, long, ulong);
+
+/* arch */
+extern void archreboot(void);
+extern char *cputype2name(char*, int);
+extern void cpuidprint(void);
+extern void uartconsinit(void);
+extern void links(void);
+extern int getncpus(void);
+extern int startcpu(uint);
+extern void okay(int);
+extern void wdogoff(void);
+
+/* devarch */
+Dirtab*	addarchfile(char*, int, long(*)(Chan*, void*, long, vlong), 
+	long(*)(Chan*, void*, long, vlong));
+
+/* dma */
+extern uintptr dmaaddr(void*);
+extern void dmaflush(int, void*, ulong);
+extern void dmastart(int, int, int, void*, void*, int);
+extern int dmawait(int);
+
+/* vcore */
+extern void* fbinit(int set, int *width, int *height, int *depth);
+extern int fbblank(int blank);
+extern void setpower(int dev, int on);
+extern int getpower(int dev);
+extern char* getethermac(void);
+extern uint getboardrev(void);
+extern uint getfirmware(void);
+extern void getramsize(Confmem *mem);
+extern ulong getclkrate(int clkid);
+extern void setclkrate(int clkid, ulong hz);
+extern uint getcputemp(void);
+extern void vgpinit(void);
+extern void vgpset(uint port, int on);
+extern void egpset(uint port, int on);
+extern int xhcireset(int devaddr);
+
+/* bootargs */
+extern void bootargsinit(uintptr);
+extern char *getconf(char *name);
+extern void setconfenv(void);
+extern void writeconf(void);
+
+/* screen */
+extern void screeninit(void);
+
+extern int isaconfig(char*, int, ISAConf*);
+
+/* pcibcm */
+extern int pcicfgrw8(int tbdf, int rno, int data, int read);
+extern int pcicfgrw16(int tbdf, int rno, int data, int read);
+extern int pcicfgrw32(int tbdf, int rno, int data, int read);
+extern void pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a);
+extern void pciintrdisable(int tbdf, void (*f)(Ureg*, void*), void *a);
--- /dev/null
+++ b/v1/9/i2c/bcm64/pi3
@@ -1,0 +1,59 @@
+dev
+	root
+	cons
+	swap
+	env
+	pipe
+	proc
+	mnt
+	srv
+	shr
+	dup
+	arch
+	tls
+	cap
+	fs
+	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium inferno
+	draw	screen swcursor
+	mouse	mouse
+	uart	gpio
+	gpio	gpio
+	i2c		i2c
+	rtc7940x	i2c
+	sd
+	usb
+	dtracy
+
+link
+	loopbackmedium
+	ethermedium
+	archbcm3
+	usbdwc
+
+ip
+	tcp
+	udp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+misc
+	uartmini
+	uartpl011
+	sdmmc	emmc
+	dma
+	vcore
+	irq
+
+	dtracysys
+	dtracytimer
+
+port
+	int cpuserver = 0;
+
+bootdir
+	/$objtype/bin/paqfs
+	/$objtype/bin/auth/factotum
+	bootfs.paq
+	boot