shithub: riscv

Download patch

ref: bc610a1b1c32f6e2e9b034217bb3ce9a7defa739
parent: ea108c8ca6e726ac008f75775ab83775ec233171
author: cinap_lenrek <[email protected]>
date: Sat Jan 26 12:33:56 EST 2013

add raspberry pi kernel (from sources)

--- /dev/null
+++ b/sys/src/9/bcm/arch.c
@@ -1,0 +1,1 @@
+#include "../omap/arch.c"
--- /dev/null
+++ b/sys/src/9/bcm/archbcm.c
@@ -1,0 +1,60 @@
+/*
+ * bcm2835 (e.g. raspberry pi) architecture-specific stuff
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "arm.h"
+
+#define	POWERREGS	(VIRTIO+0x100000)
+
+enum {
+	Wdogfreq	= 65536,
+};
+
+/*
+ * Power management / watchdog registers
+ */
+enum {
+	Rstc		= 0x1c>>2,
+		Password	= 0x5A<<24,
+		CfgMask		= 0x03<<4,
+		CfgReset	= 0x02<<4,
+	Wdog		= 0x24>>2,
+};
+
+void
+archreset(void)
+{
+	fpon();
+}
+
+void
+archreboot(void)
+{
+	u32int *r;
+
+	r = (u32int*)POWERREGS;
+	r[Wdog] = Password | 1;
+	r[Rstc] = Password | (r[Rstc] & ~CfgMask) | CfgReset;
+	coherence();
+	for(;;)
+		;
+}
+
+void
+cpuidprint(void)
+{
+	print("cpu%d: %dMHz ARM1176JZF-S\n", m->machno, m->cpumhz);
+}
+
+void
+archbcmlink(void)
+{
+}
+
--- /dev/null
+++ b/sys/src/9/bcm/arm.h
@@ -1,0 +1,273 @@
+/*
+ * arm-specific definitions for armv6
+ * these are used in C and assembler
+ */
+
+/*
+ * Program Status Registers
+ */
+#define PsrMusr		0x00000010		/* mode */
+#define PsrMfiq		0x00000011
+#define PsrMirq		0x00000012
+#define PsrMsvc		0x00000013	/* `protected mode for OS' */
+#define PsrMmon		0x00000016	/* `secure monitor' (trustzone hyper) */
+#define PsrMabt		0x00000017
+#define PsrMund		0x0000001B
+#define PsrMsys		0x0000001F	/* `privileged user mode for OS' (trustzone) */
+#define PsrMask		0x0000001F
+
+#define PsrDfiq		0x00000040		/* disable FIQ interrupts */
+#define PsrDirq		0x00000080		/* disable IRQ interrupts */
+
+#define PsrV		0x10000000		/* overflow */
+#define PsrC		0x20000000		/* carry/borrow/extend */
+#define PsrZ		0x40000000		/* zero */
+#define PsrN		0x80000000		/* negative/less than */
+
+/* instruction decoding */
+#define ISCPOP(op)	((op) == 0xE || ((op) & ~1) == 0xC)
+#define ISFPAOP(cp, op)	((cp) == CpOFPA && ISCPOP(op))
+#define ISVFPOP(cp, op)	(((cp) == CpDFP || (cp) == CpFP) && ISCPOP(op))
+
+/*
+ * Coprocessors
+ */
+#define CpOFPA		1			/* ancient 7500 FPA */
+#define CpFP		10			/* float FP, VFP cfg. */
+#define CpDFP		11			/* double FP */
+#define CpSC		15			/* System Control */
+
+/*
+ * Primary (CRn) CpSC registers.
+ */
+#define	CpID		0			/* ID and cache type */
+#define	CpCONTROL	1			/* miscellaneous control */
+#define	CpTTB		2			/* Translation Table Base(s) */
+#define	CpDAC		3			/* Domain Access Control */
+#define	CpFSR		5			/* Fault Status */
+#define	CpFAR		6			/* Fault Address */
+#define	CpCACHE		7			/* cache/write buffer control */
+#define	CpTLB		8			/* TLB control */
+#define	CpCLD		9			/* L2 Cache Lockdown, op1==1 */
+#define CpTLD		10			/* TLB Lockdown, with op2 */
+#define CpVECS		12			/* vector bases, op1==0, Crm==0, op2s (cortex) */
+#define	CpPID		13			/* Process ID */
+#define CpSPM		15			/* system performance monitor (arm1176) */
+
+/*
+ * CpTTB op1==0, Crm==0 opcode2 values.
+ */
+#define CpTTB0		0
+#define CpTTB1		1			/* cortex */
+#define CpTTBctl	2			/* cortex */
+
+/*
+ * CpFSR opcode2 values.
+ */
+#define	CpFSRdata	0			/* armv6, armv7 */
+#define	CpFSRinst	1			/* armv6, armv7 */
+
+/*
+ * CpID Secondary (CRm) registers.
+ */
+#define CpIDidct	0
+
+/*
+ * CpID op1==0 opcode2 fields.
+ * the cortex has more op1 codes for cache size, etc.
+ */
+#define CpIDid		0			/* main ID */
+#define CpIDct		1			/* cache type */
+#define CpIDtlb		3			/* tlb type (cortex) */
+#define CpIDmpid	5			/* multiprocessor id (cortex) */
+
+/* CpIDid op1 values */
+#define CpIDcsize	1			/* cache size (cortex) */
+#define CpIDcssel	2			/* cache size select (cortex) */
+
+/*
+ * CpCONTROL op2 codes, op1==0, Crm==0.
+ */
+#define CpMainctl	0
+#define CpAuxctl	1
+#define CpCPaccess	2
+
+/*
+ * CpCONTROL: op1==0, CRm==0, op2==CpMainctl.
+ * main control register.
+ * cortex/armv7 has more ops and CRm values.
+ */
+#define CpCmmu		0x00000001	/* M: MMU enable */
+#define CpCalign	0x00000002	/* A: alignment fault enable */
+#define CpCdcache	0x00000004	/* C: data cache on */
+#define CpCsbo (3<<22|1<<18|1<<16|017<<3)	/* must be 1 (armv7) */
+#define CpCsbz (CpCtre|1<<26|CpCve|1<<15|7<<7)	/* must be 0 (armv7) */
+#define CpCsw		(1<<10)		/* SW: SWP(B) enable (deprecated in v7) */
+#define CpCpredict	0x00000800	/* Z: branch prediction (armv7) */
+#define CpCicache	0x00001000	/* I: instruction cache on */
+#define CpChv		0x00002000	/* V: high vectors */
+#define CpCrr		(1<<14)	/* RR: round robin vs random cache replacement */
+#define CpCha		(1<<17)		/* HA: hw access flag enable */
+#define CpCdz		(1<<19)		/* DZ: divide by zero fault enable */
+#define CpCfi		(1<<21)		/* FI: fast intrs */
+#define CpCve		(1<<24)		/* VE: intr vectors enable */
+#define CpCee		(1<<25)		/* EE: exception endianness */
+#define CpCnmfi		(1<<27)		/* NMFI: non-maskable fast intrs. */
+#define CpCtre		(1<<28)		/* TRE: TEX remap enable */
+#define CpCafe		(1<<29)		/* AFE: access flag (ttb) enable */
+
+/*
+ * CpCONTROL: op1==0, CRm==0, op2==CpAuxctl.
+ * Auxiliary control register on cortex at least.
+ */
+#define CpACcachenopipe		(1<<20)	/* don't pipeline cache maint. */
+#define CpACcp15serial		(1<<18)	/* serialise CP1[45] ops. */
+#define CpACcp15waitidle	(1<<17)	/* CP1[45] wait-on-idle */
+#define CpACcp15pipeflush	(1<<16)	/* CP1[45] flush pipeline */
+#define CpACneonissue1		(1<<12)	/* neon single issue */
+#define CpACldstissue1		(1<<11)	/* force single issue ld, st */
+#define CpACissue1		(1<<10)	/* force single issue */
+#define CpACnobsm		(1<<7)	/* no branch size mispredicts */
+#define CpACibe			(1<<6)	/* cp15 invalidate & btb enable */
+#define CpACl1neon		(1<<5)	/* cache neon (FP) data in L1 cache */
+#define CpACasa			(1<<4)	/* enable speculative accesses */
+#define CpACl1pe		(1<<3)	/* l1 cache parity enable */
+#define CpACl2en		(1<<1)	/* l2 cache enable; default 1 */
+/*
+ * CpCONTROL Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpCONTROLscr	1
+
+#define CpSCRscr	0
+
+/*
+ * CpCACHE Secondary (CRm) registers and opcode2 fields.  op1==0.
+ * In ARM-speak, 'flush' means invalidate and 'clean' means writeback.
+ */
+#define CpCACHEintr	0			/* interrupt (op2==4) */
+#define CpCACHEisi	1			/* inner-sharable I cache (v7) */
+#define CpCACHEpaddr	4			/* 0: phys. addr (cortex) */
+#define CpCACHEinvi	5			/* instruction, branch table */
+#define CpCACHEinvd	6			/* data or unified */
+#define CpCACHEinvu	7			/* unified (not on cortex) */
+#define CpCACHEva2pa	8			/* va -> pa translation (cortex) */
+#define CpCACHEwb	10			/* writeback */
+#define CpCACHEinvdse	11			/* data or unified by mva */
+#define CpCACHEwbi	14			/* writeback+invalidate */
+
+#define CpCACHEall	0			/* entire (not for invd nor wb(i) on cortex) */
+#define CpCACHEse	1			/* single entry */
+#define CpCACHEsi	2			/* set/index (set/way) */
+#define CpCACHEtest	3			/* test loop */
+#define CpCACHEwait	4			/* wait (prefetch flush on cortex) */
+#define CpCACHEdmbarr	5			/* wb only (cortex) */
+#define CpCACHEflushbtc	6			/* flush branch-target cache (cortex) */
+#define CpCACHEflushbtse 7			/* ⋯ or just one entry in it (cortex) */
+
+/*
+ * CpTLB Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpTLBinvi	5			/* instruction */
+#define CpTLBinvd	6			/* data */
+#define CpTLBinvu	7			/* unified */
+
+#define CpTLBinv	0			/* invalidate all */
+#define CpTLBinvse	1			/* invalidate single entry */
+#define CpTBLasid	2			/* by ASID (cortex) */
+
+/*
+ * CpCLD Secondary (CRm) registers and opcode2 fields for op1==0. (cortex)
+ */
+#define CpCLDena	12			/* enables */
+#define CpCLDcyc	13			/* cycle counter */
+#define CpCLDuser	14			/* user enable */
+
+#define CpCLDenapmnc	0
+#define CpCLDenacyc	1
+
+/*
+ * CpCLD Secondary (CRm) registers and opcode2 fields for op1==1.
+ */
+#define CpCLDl2		0			/* l2 cache */
+
+#define CpCLDl2aux	2			/* auxiliary control */
+
+/*
+ * l2 cache aux. control
+ */
+#define CpCl2ecc	(1<<28)			/* use ecc, not parity */
+#define CpCl2noldforw	(1<<27)			/* no ld forwarding */
+#define CpCl2nowrcomb	(1<<25)			/* no write combining */
+#define CpCl2nowralldel	(1<<24)			/* no write allocate delay */
+#define CpCl2nowrallcomb (1<<23)		/* no write allocate combine */
+#define CpCl2nowralloc	(1<<22)			/* no write allocate */
+#define CpCl2eccparity	(1<<21)			/* enable ecc or parity */
+#define CpCl2inner	(1<<16)			/* inner cacheability */
+/* other bits are tag ram & data ram latencies */
+
+/*
+ * CpTLD Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpTLDlock	0			/* TLB lockdown registers */
+#define CpTLDpreload	1			/* TLB preload */
+
+#define CpTLDi		0			/* TLB instr. lockdown reg. */
+#define CpTLDd		1			/* " data " " */
+
+/*
+ * CpVECS Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpVECSbase	0
+
+#define CpVECSnorm	0			/* (non-)secure base addr */
+#define CpVECSmon	1			/* secure monitor base addr */
+
+/*
+ * CpSPM Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpSPMperf	12			/* various counters */
+
+#define CpSPMctl	0			/* performance monitor control */
+#define	CpSPMcyc	1			/* cycle counter register */
+
+/*
+ * CpCACHERANGE opcode2 fields for MCRR instruction (armv6)
+ */
+#define	CpCACHERANGEinvi	5		/* invalidate instruction  */
+#define	CpCACHERANGEinvd	6		/* invalidate data */
+#define CpCACHERANGEdwb		12		/* writeback */
+#define CpCACHERANGEdwbi	14		/* writeback+invalidate */
+
+/*
+ * MMU page table entries.
+ * Mbz (0x10) bit is implementation-defined and must be 0 on the cortex.
+ */
+#define Mbz		(0<<4)
+#define Fault		0x00000000		/* L[12] pte: unmapped */
+
+#define Coarse		(Mbz|1)			/* L1 */
+#define Section		(Mbz|2)			/* L1 1MB */
+#define Fine		(Mbz|3)			/* L1 */
+
+#define Large		0x00000001		/* L2 64KB */
+#define Small		0x00000002		/* L2 4KB */
+#define Tiny		0x00000003		/* L2 1KB: not in v7 */
+#define Buffered	0x00000004		/* L[12]: write-back not -thru */
+#define Cached		0x00000008		/* L[12] */
+#define Dom0		0
+
+#define Noaccess	0			/* AP, DAC */
+#define Krw		1			/* AP */
+/* armv7 deprecates AP[2] == 1 & AP[1:0] == 2 (Uro), prefers 3 (new in v7) */
+#define Uro		2			/* AP */
+#define Urw		3			/* AP */
+#define Client		1			/* DAC */
+#define Manager		3			/* DAC */
+
+#define F(v, o, w)	(((v) & ((1<<(w))-1))<<(o))
+#define AP(n, v)	F((v), ((n)*2)+4, 2)
+#define L1AP(ap)	(AP(3, (ap)))
+#define L2AP(ap) (AP(3, (ap))|AP(2, (ap))|AP(1, (ap))|AP(0, (ap))) /* pre-armv7 */
+#define DAC(n, v)	F((v), (n)*2, 2)
+
+#define HVECTORS	0xffff0000
--- /dev/null
+++ b/sys/src/9/bcm/arm.s
@@ -1,0 +1,36 @@
+/*
+ * armv6 machine assist, definitions
+ *
+ * loader uses R11 as scratch.
+ */
+
+#include "mem.h"
+#include "arm.h"
+
+#define PADDR(va)	(PHYSDRAM | ((va) & ~KSEGM))
+
+#define L1X(va)		(((((va))>>20) & 0x0fff)<<2)
+
+#define PTEDRAM		(Dom0|L1AP(Krw)|Section|Cached|Buffered)
+
+/*
+ * new instructions
+ */
+
+#define ISB	\
+	MOVW	$0, R0; \
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEwait
+
+#define DSB \
+	MOVW	$0, R0; \
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait
+
+#define	BARRIERS	ISB; DSB
+
+#define MCRR(coproc, op, rd, rn, crm) \
+	WORD $(0xec400000|(rn)<<16|(rd)<<12|(coproc)<<8|(op)<<4|(crm))
+
+#define OKAY \
+	MOVW	$0x7E200028,R2; \
+	MOVW	$0x10000,R3; \
+	MOVW	R3,(R2)
--- /dev/null
+++ b/sys/src/9/bcm/clock.c
@@ -1,0 +1,204 @@
+/*
+ * bcm2835 timers
+ *	System timers run at 1MHz (timers 1 and 2 are used by GPU)
+ *	ARM timer usually runs at 250MHz (may be slower in low power modes)
+ *	Cycle counter runs at 700MHz (unless overclocked)
+ *    All are free-running up-counters
+ *
+ * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
+ * Use ARM timer (32 bits) for perfticks
+ * Use ARM timer to force immediate interrupt
+ * Use cycle counter for cycles()
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+enum {
+	SYSTIMERS	= VIRTIO+0x3000,
+	ARMTIMER	= VIRTIO+0xB400,
+
+	SystimerFreq	= 1*Mhz,
+	MaxPeriod	= SystimerFreq / HZ,
+	MinPeriod	= SystimerFreq / (100*HZ),
+};
+
+typedef struct Systimers Systimers;
+typedef struct Armtimer Armtimer;
+
+struct Systimers {
+	u32int	cs;
+	u32int	clo;
+	u32int	chi;
+	u32int	c0;
+	u32int	c1;
+	u32int	c2;
+	u32int	c3;
+};
+
+struct Armtimer {
+	u32int	load;
+	u32int	val;
+	u32int	ctl;
+	u32int	irqack;
+	u32int	irq;
+	u32int	maskedirq;
+	u32int	reload;
+	u32int	predivider;
+	u32int	count;
+};
+
+enum {
+	CntPrescaleShift= 16,	/* freq is sys_clk/(prescale+1) */
+	CntPrescaleMask	= 0xFF,
+	CntEnable	= 1<<9,
+	TmrDbgHalt	= 1<<8,
+	TmrEnable	= 1<<7,
+	TmrIntEnable	= 1<<5,
+	TmrPrescale1	= 0x00<<2,
+	TmrPrescale16	= 0x01<<2,
+	TmrPrescale256	= 0x02<<2,
+	CntWidth16	= 0<<1,
+	CntWidth32	= 1<<1,
+};
+
+static void
+clockintr(Ureg *ureg, void *)
+{
+	Systimers *tn;
+
+	tn = (Systimers*)SYSTIMERS;
+	/* dismiss interrupt */
+	tn->cs = 1<<3;
+	timerintr(ureg, 0);
+}
+
+void
+clockshutdown(void)
+{
+	Armtimer *tm;
+
+	tm = (Armtimer*)ARMTIMER;
+	tm->ctl = 0;
+}
+
+void
+clockinit(void)
+{
+	Systimers *tn;
+	Armtimer *tm;
+	u32int t0, t1, tstart, tend;
+
+	tn = (Systimers*)SYSTIMERS;
+	tm = (Armtimer*)ARMTIMER;
+	tm->load = 0;
+	tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
+	coherence();
+
+	tstart = tn->clo;
+	do{
+		t0 = lcycles();
+	}while(tn->clo == tstart);
+	tend = tstart + 10000;
+	do{
+		t1 = lcycles();
+	}while(tn->clo != tend);
+	t1 -= t0;
+	m->cpuhz = 100 * t1;
+	m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
+	m->cyclefreq = m->cpuhz;
+
+	tn->c3 = tn->clo - 1;
+	intrenable(IRQtimer3, clockintr, nil, 0, "clock");
+}
+
+void
+timerset(uvlong next)
+{
+	Systimers *tn;
+	vlong now, period;
+
+	tn = (Systimers*)SYSTIMERS;
+	now = fastticks(nil);
+	period = next - fastticks(nil);
+	if(period < MinPeriod)
+		next = now + MinPeriod;
+	else if(period > MaxPeriod)
+		next = now + MaxPeriod;
+	tn->c3 = (ulong)next;
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	Systimers *tn;
+	ulong lo, hi;
+
+	tn = (Systimers*)SYSTIMERS;
+	if(hz)
+		*hz = SystimerFreq;
+	do{
+		hi = tn->chi;
+		lo = tn->clo;
+	}while(tn->chi != hi);
+	m->fastclock = (uvlong)hi<<32 | lo;
+	return m->fastclock;
+}
+
+ulong
+perfticks(void)
+{
+	Armtimer *tm;
+
+	tm = (Armtimer*)ARMTIMER;
+	return tm->count;
+}
+
+void
+armtimerset(int n)
+{
+	Armtimer *tm;
+
+	tm = (Armtimer*)ARMTIMER;
+	if(n > 0){
+		tm->ctl |= TmrEnable|TmrIntEnable;
+		tm->load = n;
+	}else{
+		tm->load = 0;
+		tm->ctl &= ~(TmrEnable|TmrIntEnable);
+		tm->irq = 1;
+	}
+	coherence();
+}
+
+ulong
+µs(void)
+{
+	if(SystimerFreq != 1*Mhz)
+		return fastticks2us(fastticks(nil));
+	return fastticks(nil);
+}
+
+void
+microdelay(int n)
+{
+	Systimers *tn;
+	u32int now, diff;
+
+	tn = (Systimers*)SYSTIMERS;
+	diff = n + 1;
+	now = tn->clo;
+	while(tn->clo - now < diff)
+		;
+}
+
+void
+delay(int n)
+{
+	while(--n >= 0)
+		microdelay(1000);
+}
--- /dev/null
+++ b/sys/src/9/bcm/coproc.c
@@ -1,0 +1,1 @@
+#include "../teg2/coproc.c"
--- /dev/null
+++ b/sys/src/9/bcm/dat.h
@@ -1,0 +1,287 @@
+/*
+ * 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 ISAConf	ISAConf;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Memcache	Memcache;
+typedef struct MMMU	MMMU;
+typedef struct Mach	Mach;
+typedef struct Notsave	Notsave;
+typedef struct Page	Page;
+typedef struct PhysUart	PhysUart;
+typedef struct PMMU	PMMU;
+typedef struct Proc	Proc;
+typedef u32int		PTE;
+typedef struct Uart	Uart;
+typedef struct Ureg	Ureg;
+typedef uvlong		Tval;
+
+#pragma incomplete Ureg
+
+#define MAXSYSARG	5	/* for mount(fd, mpt, flag, arg, srv) */
+
+/*
+ *  parameters for sysproc.c
+ */
+#define AOUT_MAGIC	(E_MAGIC)
+
+struct Lock
+{
+	ulong	key;
+	u32int	sr;
+	uintptr	pc;
+	Proc*	p;
+	Mach*	m;
+	int	isilock;
+};
+
+struct Label
+{
+	uintptr	sp;
+	uintptr	pc;
+};
+
+enum {
+	Maxfpregs	= 32,	/* could be 16 or 32, see Mach.fpnregs */
+	Nfpctlregs	= 16,
+};
+
+/*
+ * emulated or vfp3 floating point
+ */
+struct FPsave
+{
+	ulong	status;
+	ulong	control;
+	/*
+	 * vfp3 with ieee fp regs; uvlong is sufficient for hardware but
+	 * each must be able to hold an Internal from fpi.h for sw emulation.
+	 */
+	ulong	regs[Maxfpregs][3];
+
+	int	fpstate;
+	uintptr	pc;		/* of failed fp instr. */
+};
+
+/*
+ * FPsave.fpstate
+ */
+enum
+{
+	FPinit,
+	FPactive,
+	FPinactive,
+	FPemu,
+
+	/* bits or'd with the state */
+	FPillegal= 0x100,
+};
+
+struct Confmem
+{
+	uintptr	base;
+	usize	npage;
+	uintptr	limit;
+	uintptr	kbase;
+	uintptr	klimit;
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	Confmem	mem[1];		/* physical memory */
+	ulong	npage;		/* total physical pages of memory */
+	usize	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 */
+};
+
+/*
+ *  things saved in the Proc structure during a notify
+ */
+struct Notsave {
+	int	emptiness;
+};
+
+/*
+ *  MMU stuff in Mach.
+ */
+struct MMMU
+{
+	PTE*	mmul1;		/* l1 for this processor */
+	int	mmul1lo;
+	int	mmul1hi;
+	int	mmupid;
+};
+
+/*
+ *  MMU stuff in proc
+ */
+#define NCOLOR	1		/* 1 level cache, don't worry about VCE's */
+struct PMMU
+{
+	Page*	mmul2;
+	Page*	mmul2cache;	/* free mmu pages */
+};
+
+#include "../port/portdat.h"
+
+struct Mach
+{
+	int	machno;			/* physical id of processor */
+	uintptr	splpc;			/* pc of last caller to splhi */
+
+	Proc*	proc;			/* current process */
+
+	MMMU;
+	int	flushmmu;		/* flush current proc mmu state */
+
+	ulong	ticks;			/* of the clock since boot time */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void*	alarm;			/* alarms bound to this clock */
+
+	Proc*	readied;		/* for runproc */
+	ulong	schedticks;		/* next forced context switch */
+
+	int	cputype;
+	ulong	delayloop;
+
+	/* stats */
+	int	tlbfault;
+	int	tlbpurge;
+	int	pfault;
+	int	cs;
+	int	syscall;
+	int	load;
+	int	intr;
+	uvlong	fastclock;		/* last sampled value */
+	uvlong	inidle;			/* time spent in idlehands() */
+	ulong	spuriousintr;
+	int	lastintr;
+	int	ilockdepth;
+	Perf	perf;			/* performance counters */
+
+
+	int	cpumhz;
+	uvlong	cpuhz;			/* speed of cpu */
+	uvlong	cyclefreq;		/* Frequency of user readable cycle counter */
+
+	/* vfp2 or vfp3 fpu */
+	int	havefp;
+	int	havefpvalid;
+	int	fpon;
+	int	fpconfiged;
+	int	fpnregs;
+	ulong	fpscr;			/* sw copy */
+	int	fppid;			/* pid of last fault */
+	uintptr	fppc;			/* addr of last fault */
+	int	fpcnt;			/* how many consecutive at that addr */
+
+	/* save areas for exceptions, hold R0-R4 */
+	u32int	sfiq[5];
+	u32int	sirq[5];
+	u32int	sund[5];
+	u32int	sabt[5];
+	u32int	smon[5];		/* probably not needed */
+	u32int	ssys[5];
+
+	int	stack[1];
+};
+
+/*
+ * Fake kmap.
+ */
+typedef void		KMap;
+#define	VA(k)		((uintptr)(k))
+#define	kmap(p)		(KMap*)((p)->pa|kseg0)
+#define	kunmap(k)
+
+struct
+{
+	Lock;
+	int	machs;			/* bitmap of active CPUs */
+	int	exiting;		/* shutdown */
+	int	ispanic;		/* shutdown in response to a panic */
+}active;
+
+extern register Mach* m;			/* R10 */
+extern register Proc* up;			/* R9 */
+extern uintptr kseg0;
+extern Mach* machaddr[MAXMACH];
+extern ulong memsize;
+extern int normalprint;
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define NISAOPT		8
+
+struct ISAConf {
+	char	*type;
+	ulong	port;
+	int	irq;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	*opt[NISAOPT];
+};
+
+#define	MACHP(n)	(machaddr[n])
+
+/*
+ * 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 */
+};
+
--- /dev/null
+++ b/sys/src/9/bcm/devarch.c
@@ -1,0 +1,163 @@
+#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 str[128];
+
+	snprint(str, sizeof str, "ARM11 %d\n", m->cpumhz);
+	return readstr(offset, a, n, str);
+}
+
+void
+archinit(void)
+{
+	addarchfile("cputype", 0444, cputyperead, nil);
+}
--- /dev/null
+++ b/sys/src/9/bcm/devfakertc.c
@@ -1,0 +1,124 @@
+/*
+ * raspberry pi doesn't have a realtime clock
+ * fake a crude approximation from the kernel build time
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+enum{
+	Qdir = 0,
+	Qrtc,
+};
+
+Dirtab rtcdir[]={
+	".",	{Qdir, 0, QTDIR},	0,	0555,
+	"rtc",		{Qrtc, 0},	0,	0664,
+};
+
+extern ulong kerndate;
+
+static ulong rtcsecs;
+
+static void
+rtctick(void)
+{
+	rtcsecs++;
+}
+
+static void
+rtcinit(void)
+{
+	rtcsecs = kerndate;
+	addclock0link(rtctick, 1000);
+}
+
+static long
+rtcread(Chan *c, void *a, long n, vlong offset)
+{
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, rtcdir, nelem(rtcdir), devgen);
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		return readnum((ulong)offset, a, n, rtcsecs, 12);
+	}
+	error(Ebadarg);
+	return 0;
+}
+
+static long
+rtcwrite(Chan*c, void *a, long n, vlong)
+{
+	char b[13];
+	ulong i;
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		if(n >= sizeof(b))
+			error(Ebadarg);
+		strncpy(b, (char*)a, n);
+		i = strtol(b, 0, 0);
+		if(i <= 0)
+			error(Ebadarg);
+		rtcsecs = i;
+		return n;
+	}
+	error(Eperm);
+	return 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)
+{
+	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
+}
+
+static void	 
+rtcclose(Chan*)
+{
+}
+
+Dev fakertcdevtab = {
+	'r',
+	"rtc",
+
+	devreset,
+	rtcinit,
+	devshutdown,
+	rtcattach,
+	rtcwalk,
+	rtcstat,
+	rtcopen,
+	devcreate,
+	rtcclose,
+	rtcread,
+	devbread,
+	rtcwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
--- /dev/null
+++ b/sys/src/9/bcm/dma.c
@@ -1,0 +1,221 @@
+/*
+ * bcm2835 dma controller
+ *
+ * simplest to use only channels 0-6
+ *	channels 7-14 have reduced functionality
+ *	channel 15 is at a weird address
+ *	channels 0 and 15 have an "external 128 bit 8 word read FIFO"
+ *	  for memory to memory transfers
+ *
+ * Experiments show that only channels 2-5,11-12 work with mmc
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define DMAREGS	(VIRTIO+0x7000)
+
+#define DBG	if(Dbg)
+
+enum {
+	Nchan		= 7,		/* number of dma channels */
+	Regsize		= 0x100,	/* size of regs for each chan */
+	Cbalign		= 32,		/* control block byte alignment */
+	Dbg		= 0,
+	
+	/* registers for each dma controller */
+	Cs		= 0x00>>2,
+	Conblkad	= 0x04>>2,
+	Ti		= 0x08>>2,
+	Sourcead	= 0x0c>>2,
+	Destad		= 0x10>>2,
+	Txfrlen		= 0x14>>2,
+	Stride		= 0x18>>2,
+	Nextconbk	= 0x1c>>2,
+	Debug		= 0x20>>2,
+
+	/* collective registers */
+	Intstatus	= 0xfe0>>2,
+	Enable		= 0xff0>>2,
+
+	/* Cs */
+	Reset		= 1<<31,
+	Abort		= 1<<30,
+	Error		= 1<<8,
+	Waitwrite	= 1<<6,
+	Waitdreq	= 1<<5,
+	Paused		= 1<<4,
+	Dreq		= 1<<3,
+	Int		= 1<<2,
+	End		= 1<<1,
+	Active		= 1<<0,
+
+	/* Ti */
+	Permapshift= 16,
+	Srcignore	= 1<<11,
+	Srcdreq		= 1<<10,
+	Srcwidth128	= 1<<9,
+	Srcinc		= 1<<8,
+	Destignore	= 1<<7,
+	Destdreq	= 1<<6,
+	Destwidth128	= 1<<5,
+	Destinc		= 1<<4,
+	Waitresp	= 1<<3,
+	Tdmode		= 1<<1,
+	Inten		= 1<<0,
+
+	/* Debug */
+	Lite		= 1<<28,
+	Clrerrors	= 7<<0,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Cb Cb;
+
+struct Ctlr {
+	u32int	*regs;
+	Cb	*cb;
+	Rendez	r;
+	int	dmadone;
+};
+
+struct Cb {
+	u32int	ti;
+	u32int	sourcead;
+	u32int	destad;
+	u32int	txfrlen;
+	u32int	stride;
+	u32int	nextconbk;
+	u32int	reserved[2];
+};
+
+static Ctlr dma[Nchan];
+static u32int *dmaregs = (u32int*)DMAREGS;
+
+static void
+dump(char *msg, uchar *p, int n)
+{
+	print("%s", msg);
+	while(n-- > 0)
+		print(" %2.2x", *p++);
+	print("\n");
+}
+
+static void
+dumpdregs(char *msg, u32int *r)
+{
+	int i;
+
+	print("%s: %#p =", msg, r);
+	for(i = 0; i < 9; i++)
+		print(" %8.8uX", r[i]);
+	print("\n");
+}
+
+static int
+dmadone(void *a)
+{
+	return ((Ctlr*)a)->dmadone;
+}
+
+static void
+dmainterrupt(Ureg*, void *a)
+{
+	Ctlr *ctlr;
+
+	ctlr = a;
+	ctlr->regs[Cs] = Int;
+	ctlr->dmadone = 1;
+	wakeup(&ctlr->r);
+}
+
+void
+dmastart(int chan, int dev, int dir, void *src, void *dst, int len)
+{
+	Ctlr *ctlr;
+	Cb *cb;
+	int ti;
+
+	ctlr = &dma[chan];
+	if(ctlr->regs == nil){
+		ctlr->regs = (u32int*)(DMAREGS + chan*Regsize);
+		ctlr->cb = xspanalloc(sizeof(Cb), Cbalign, 0);
+		assert(ctlr->cb != nil);
+		dmaregs[Enable] |= 1 << chan;
+		ctlr->regs[Cs] = Reset;
+		while(ctlr->regs[Cs] & Reset)
+			;
+		intrenable(IRQDMA(chan), dmainterrupt, ctlr, 0, "dma");
+	}
+	cb = ctlr->cb;
+	ti = 0;
+	switch(dir){
+	case DmaD2M:
+		cachedwbinvse(dst, len);
+		ti = Srcdreq | Destinc;
+		cb->sourcead = DMAIO(src);
+		cb->destad = DMAADDR(dst);
+		break;
+	case DmaM2D:
+		cachedwbse(src, len);
+		ti = Destdreq | Srcinc;
+		cb->sourcead = DMAADDR(src);
+		cb->destad = DMAIO(dst);
+		break;
+	case DmaM2M:
+		cachedwbse(src, len);
+		cachedwbinvse(dst, len);
+		ti = Srcinc | Destinc;
+		cb->sourcead = DMAADDR(src);
+		cb->destad = DMAADDR(dst);
+		break;
+	}
+	cb->ti = ti | dev << Permapshift | Inten;
+	cb->txfrlen = len;
+	cb->stride = 0;
+	cb->nextconbk = 0;
+	cachedwbse(cb, sizeof(Cb));
+	ctlr->regs[Cs] = 0;
+	microdelay(1);
+	ctlr->regs[Conblkad] = DMAADDR(cb);
+	DBG print("dma start: %ux %ux %ux %ux %ux %ux\n",
+		cb->ti, cb->sourcead, cb->destad, cb->txfrlen,
+		cb->stride, cb->nextconbk);
+	DBG print("intstatus %ux\n", dmaregs[Intstatus]);
+	dmaregs[Intstatus] = 0;
+	ctlr->regs[Cs] = Int;
+	microdelay(1);
+	coherence();
+	DBG dumpdregs("before Active", ctlr->regs);
+	ctlr->regs[Cs] = Active;
+	DBG dumpdregs("after Active", ctlr->regs);
+}
+
+int
+dmawait(int chan)
+{
+	Ctlr *ctlr;
+	u32int *r;
+	int s;
+
+	ctlr = &dma[chan];
+	tsleep(&ctlr->r, dmadone, ctlr, 3000);
+	ctlr->dmadone = 0;
+	r = ctlr->regs;
+	DBG dumpdregs("after sleep", r);
+	s = r[Cs];
+	if((s & (Active|End|Error)) != End){
+		print("dma chan %d %s Cs %ux Debug %ux\n", chan,
+			(s&End)? "error" : "timeout", s, r[Debug]);
+		r[Cs] = Reset;
+		r[Debug] = Clrerrors;
+		return -1;
+	}
+	r[Cs] = Int|End;
+	return 0;
+}
--- /dev/null
+++ b/sys/src/9/bcm/dwcotg.h
@@ -1,0 +1,510 @@
+/*
+ * USB host driver for BCM2835
+ *	Synopsis DesignWare Core USB 2.0 OTG controller
+ *
+ * Device register definitions
+ */
+
+typedef unsigned int Reg;
+typedef struct Dwcregs Dwcregs;
+typedef struct Hostchan Hostchan;
+
+enum {
+	Maxchans	= 16,	/* actual number of channels in ghwcfg2 */
+};
+
+struct Dwcregs {
+	/* Core global registers 0x000-0x140 */
+	Reg	gotgctl;	/* OTG Control and Status */
+	Reg	gotgint;	/* OTG Interrupt */
+	Reg	gahbcfg;	/* Core AHB Configuration */
+	Reg	gusbcfg;	/* Core USB Configuration */
+	Reg	grstctl;	/* Core Reset */
+	Reg	gintsts;	/* Core Interrupt */
+	Reg	gintmsk;	/* Core Interrupt Mask */
+	Reg	grxstsr;	/* Receive Status Queue Read (RO) */
+	Reg	grxstsp;	/* Receive Status Queue Read & POP (RO) */
+	Reg	grxfsiz;	/* Receive FIFO Size */
+	Reg	gnptxfsiz;	/* Non Periodic Transmit FIFO Size */
+	Reg	gnptxsts;	/* Non Periodic Transmit FIFO/Queue Status (RO) */
+	Reg	gi2cctl;	/* I2C Access */
+	Reg	gpvndctl;	/* PHY Vendor Control */
+	Reg	ggpio;		/* General Purpose Input/Output */
+	Reg	guid;		/* User ID */
+	Reg	gsnpsid;	/* Synopsys ID (RO) */
+	Reg	ghwcfg1;	/* User HW Config1 (RO) (DEVICE) */
+	Reg	ghwcfg2;	/* User HW Config2 (RO) */
+	Reg	ghwcfg3;	/* User HW Config3 (RO) */
+	Reg	ghwcfg4;	/* User HW Config4 (RO)*/
+	Reg	glpmcfg;	/* Core LPM Configuration */
+	Reg	gpwrdn;		/* Global PowerDn */
+	Reg	gdfifocfg;	/* Global DFIFO SW Config (DEVICE?) */
+	Reg	adpctl;		/* ADP Control */
+	Reg	reserved0[39];
+	Reg	hptxfsiz;	/* Host Periodic Transmit FIFO Size */
+	Reg	dtxfsiz[15];	/* Device Periodic Transmit FIFOs (DEVICE) */
+	char	pad0[0x400-0x140];
+
+	/* Host global registers 0x400-0x420 */
+	Reg	hcfg;		/* Configuration */
+	Reg	hfir;		/* Frame Interval */
+	Reg	hfnum;		/* Frame Number / Frame Remaining (RO) */
+	Reg	reserved1;
+	Reg	hptxsts;	/* Periodic Transmit FIFO / Queue Status */
+	Reg	haint;		/* All Channels Interrupt */
+	Reg	haintmsk;	/* All Channels Interrupt Mask */
+	Reg	hflbaddr;	/* Frame List Base Address */
+	char	pad1[0x440-0x420];
+
+	/* Host port register 0x440 */
+	Reg	hport0;		/* Host Port 0 Control and Status */
+	char	pad2[0x500-0x444];
+
+	/* Host channel specific registers 0x500-0x700 */
+	struct	Hostchan {
+		Reg	hcchar;	/* Characteristic */
+		Reg	hcsplt;	/* Split Control */
+		Reg	hcint;	/* Interrupt */
+		Reg	hcintmsk; /* Interrupt Mask */
+		Reg	hctsiz;	/* Transfer Size */
+		Reg	hcdma;	/* DMA Address */
+		Reg	reserved;
+		Reg	hcdmab;	/* DMA Buffer Address */
+	} hchan[Maxchans];
+	char	pad3[0xE00-0x700];
+
+	/* Power & clock gating control register 0xE00 */
+	Reg	pcgcctl;
+};
+
+enum {
+	/* gotgctl */
+	Sesreqscs	= 1<<0,
+	Sesreq		= 1<<1,
+	Vbvalidoven	= 1<<2,
+	Vbvalidovval	= 1<<3,
+	Avalidoven	= 1<<4,
+	Avalidovval	= 1<<5,
+	Bvalidoven	= 1<<6,
+	Bvalidovval	= 1<<7,
+	Hstnegscs	= 1<<8,
+	Hnpreq		= 1<<9,
+	Hstsethnpen	= 1<<10,
+	Devhnpen	= 1<<11,
+	Conidsts	= 1<<16,
+	Dbnctime	= 1<<17,
+	Asesvld		= 1<<18,
+	Bsesvld		= 1<<19,
+	Otgver		= 1<<20,
+	Multvalidbc	= 0x1F<<22,
+	Chirpen		= 1<<27,
+
+	/* gotgint */
+	Sesenddet	= 1<<2,
+	Sesreqsucstschng= 1<<8,
+	Hstnegsucstschng= 1<<9,
+	Hstnegdet	= 1<<17,
+	Adevtoutchng	= 1<<18,
+	Debdone		= 1<<19,
+	Mvic		= 1<<20,
+
+	/* gahbcfg */
+	Glblintrmsk	= 1<<0,
+	/* bits 1:4 redefined for BCM2835 */
+	Axiburstlen	= 0x3<<1,
+		BURST1		= 3<<1,
+		BURST2		= 2<<1,
+		BURST3		= 1<<1,
+		BURST4		= 0<<1,
+	Axiwaitwrites	= 1<<4,
+	Dmaenable	= 1<<5,
+	Nptxfemplvl	= 1<<7,
+		NPTX_HALFEMPTY	= 0<<7,
+		NPTX_EMPTY	= 1<<7,
+	Ptxfemplvl	= 1<<8,
+		PTX_HALFEMPTY	= 0<<8,
+		PTX_EMPTY	= 1<<8,
+	Remmemsupp	= 1<<21,
+	Notialldmawrit	= 1<<22,
+	Ahbsingle	= 1<<23,
+
+	/* gusbcfg */
+	Toutcal		= 0x7<<0,
+	Phyif		= 1<<3,
+	Ulpi_utmi_sel	= 1<<4,
+	Fsintf		= 1<<5,
+		FsUnidir	= 0<<5,
+		FsBidir		= 1<<5,
+	Physel		= 1<<6,
+		PhyHighspeed	= 0<<6,
+		PhyFullspeed	= 1<<6,
+	Ddrsel		= 1<<7,
+	Srpcap		= 1<<8,
+	Hnpcap		= 1<<9,
+	Usbtrdtim	= 0xf<<10,
+		OUsbtrdtim		= 10,
+	Phylpwrclksel	= 1<<15,
+	Otgutmifssel	= 1<<16,
+	Ulpi_fsls	= 1<<17,
+	Ulpi_auto_res	= 1<<18,
+	Ulpi_clk_sus_m	= 1<<19,
+	Ulpi_ext_vbus_drv= 1<<20,
+	Ulpi_int_vbus_indicator= 1<<21,
+	Term_sel_dl_pulse= 1<<22,
+	Indicator_complement= 1<<23,
+	Indicator_pass_through= 1<<24,
+	Ulpi_int_prot_dis= 1<<25,
+	Ic_usb_cap	= 1<<26,
+	Ic_traffic_pull_remove= 1<<27,
+	Tx_end_delay	= 1<<28,
+	Force_host_mode	= 1<<29,
+	Force_dev_mode	= 1<<30,
+
+	/* grstctl */
+	Csftrst		= 1<<0,
+	Hsftrst		= 1<<1,
+	Hstfrm		= 1<<2,
+	Intknqflsh	= 1<<3,
+	Rxfflsh		= 1<<4,
+	Txfflsh		= 1<<5,
+	Txfnum		= 0x1f<<6,
+		TXF_ALL		= 0x10<<6,
+	Dmareq		= 1<<30,
+	Ahbidle		= 1<<31,
+
+	/* gintsts, gintmsk */
+	Curmode		= 1<<0,
+		HOSTMODE	= 1<<0,
+		DEVMODE		= 0<<0,
+	Modemismatch	= 1<<1,
+	Otgintr		= 1<<2,
+	Sofintr		= 1<<3,
+	Rxstsqlvl	= 1<<4,
+	Nptxfempty	= 1<<5,
+	Ginnakeff	= 1<<6,
+	Goutnakeff	= 1<<7,
+	Ulpickint	= 1<<8,
+	I2cintr		= 1<<9,
+	Erlysuspend	= 1<<10,
+	Usbsuspend	= 1<<11,
+	Usbreset	= 1<<12,
+	Enumdone	= 1<<13,
+	Isooutdrop	= 1<<14,
+	Eopframe	= 1<<15,
+	Restoredone	= 1<<16,
+	Epmismatch	= 1<<17,
+	Inepintr	= 1<<18,
+	Outepintr	= 1<<19,
+	Incomplisoin	= 1<<20,
+	Incomplisoout	= 1<<21,
+	Fetsusp		= 1<<22,
+	Resetdet	= 1<<23,
+	Portintr	= 1<<24,
+	Hcintr		= 1<<25,
+	Ptxfempty	= 1<<26,
+	Lpmtranrcvd	= 1<<27,
+	Conidstschng	= 1<<28,
+	Disconnect	= 1<<29,
+	Sessreqintr	= 1<<30,
+	Wkupintr	= 1<<31,
+
+	/* grxsts[rp] */
+	Chnum		= 0xf<<0,
+	Bcnt		= 0x7ff<<4,
+	Dpid		= 0x3<<15,
+	Pktsts		= 0xf<<17,
+		PKTSTS_IN		= 2<<17,
+		PKTSTS_IN_XFER_COMP	= 3<<17,
+		PKTSTS_DATA_TOGGLE_ERR	= 5<<17,
+		PKTSTS_CH_HALTED	= 7<<17,
+
+	/* hptxfsiz, gnptxfsiz */
+	Startaddr	= 0xffff<<0,
+	Depth		= 0xffff<<16,
+		ODepth		= 16,
+
+	/* gnptxsts */
+	Nptxfspcavail	= 0xffff<<0,
+	Nptxqspcavail	= 0xff<<16,
+	Nptxqtop_terminate= 1<<24,
+	Nptxqtop_token	= 0x3<<25,
+	Nptxqtop_chnep	= 0xf<<27,
+
+	/* gpvndctl */
+	Regdata		= 0xff<<0,
+	Vctrl		= 0xff<<8,
+	Regaddr16_21	= 0x3f<<16,
+	Regwr		= 1<<22,
+	Newregreq	= 1<<25,
+	Vstsbsy		= 1<<26,
+	Vstsdone	= 1<<27,
+	Disulpidrvr	= 1<<31,
+
+	/* ggpio */
+	Gpi		= 0xffff<<0,
+	Gpo		= 0xffff<<16,
+
+	/* ghwcfg2 */
+	Op_mode		= 0x7<<0,
+		HNP_SRP_CAPABLE_OTG	= 0<<0,
+		SRP_ONLY_CAPABLE_OTG	= 1<<0,
+		NO_HNP_SRP_CAPABLE	= 2<<0,
+		SRP_CAPABLE_DEVICE	= 3<<0,
+		NO_SRP_CAPABLE_DEVICE	= 4<<0,
+		SRP_CAPABLE_HOST	= 5<<0,
+		NO_SRP_CAPABLE_HOST	= 6<<0,
+	Architecture	= 0x3<<3,
+		SLAVE_ONLY		= 0<<3,
+		EXT_DMA			= 1<<3,
+		INT_DMA			= 2<<3,
+	Point2point	= 1<<5,
+	Hs_phy_type	= 0x3<<6,
+		PHY_NOT_SUPPORTED	= 0<<6,
+		PHY_UTMI		= 1<<6,
+		PHY_ULPI		= 2<<6,
+		PHY_UTMI_ULPI		= 3<<6,
+	Fs_phy_type	= 0x3<<8,
+	Num_dev_ep	= 0xf<<10,
+	Num_host_chan	= 0xf<<14,
+		ONum_host_chan		= 14,
+	Perio_ep_supported= 1<<18,
+	Dynamic_fifo	= 1<<19,
+	Nonperio_tx_q_depth= 0x3<<22,
+	Host_perio_tx_q_depth= 0x3<<24,
+	Dev_token_q_depth= 0x1f<<26,
+	Otg_enable_ic_usb= 1<<31,
+
+	/* ghwcfg3 */
+	Xfer_size_cntr_width	= 0xf<<0,
+	Packet_size_cntr_width	= 0x7<<4,
+	Otg_func		= 1<<7,
+	I2c			= 1<<8,
+	Vendor_ctrl_if		= 1<<9,
+	Optional_features	= 1<<10,
+	Synch_reset_type	= 1<<11,
+	Adp_supp		= 1<<12,
+	Otg_enable_hsic		= 1<<13,
+	Bc_support		= 1<<14,
+	Otg_lpm_en		= 1<<15,
+	Dfifo_depth		= 0xffff<<16,
+		ODfifo_depth		= 16,
+
+	/* ghwcfg4 */
+	Num_dev_perio_in_ep	= 0xf<<0,
+	Power_optimiz		= 1<<4,
+	Min_ahb_freq		= 1<<5,
+	Hiber			= 1<<6,
+	Xhiber			= 1<<7,
+	Utmi_phy_data_width	= 0x3<<14,
+	Num_dev_mode_ctrl_ep	= 0xf<<16,
+	Iddig_filt_en		= 1<<20,
+	Vbus_valid_filt_en	= 1<<21,
+	A_valid_filt_en		= 1<<22,
+	B_valid_filt_en		= 1<<23,
+	Session_end_filt_en	= 1<<24,
+	Ded_fifo_en		= 1<<25,
+	Num_in_eps		= 0xf<<26,
+	Desc_dma		= 1<<30,
+	Desc_dma_dyn		= 1<<31,
+
+	/* glpmcfg */
+	Lpm_cap_en	= 1<<0,
+	Appl_resp	= 1<<1,
+	Hird		= 0xf<<2,
+	Rem_wkup_en	= 1<<6,
+	En_utmi_sleep	= 1<<7,
+	Hird_thres	= 0x1f<<8,
+	Lpm_resp	= 0x3<<13,
+	Prt_sleep_sts	= 1<<15,
+	Sleep_state_resumeok= 1<<16,
+	Lpm_chan_index	= 0xf<<17,
+	Retry_count	= 0x7<<21,
+	Send_lpm	= 1<<24,
+	Retry_count_sts	= 0x7<<25,
+	Hsic_connect	= 1<<30,
+	Inv_sel_hsic	= 1<<31,
+
+	/* gpwrdn */
+	Pmuintsel	= 1<<0,
+	Pmuactv		= 1<<1,
+	Restore		= 1<<2,
+	Pwrdnclmp	= 1<<3,
+	Pwrdnrstn	= 1<<4,
+	Pwrdnswtch	= 1<<5,
+	Dis_vbus	= 1<<6,
+	Lnstschng	= 1<<7,
+	Lnstchng_msk	= 1<<8,
+	Rst_det		= 1<<9,
+	Rst_det_msk	= 1<<10,
+	Disconn_det	= 1<<11,
+	Disconn_det_msk	= 1<<12,
+	Connect_det	= 1<<13,
+	Connect_det_msk	= 1<<14,
+	Srp_det		= 1<<15,
+	Srp_det_msk	= 1<<16,
+	Sts_chngint	= 1<<17,
+	Sts_chngint_msk	= 1<<18,
+	Linestate	= 0x3<<19,
+	Idsts		= 1<<21,
+	Bsessvld	= 1<<22,
+	Adp_int		= 1<<23,
+	Mult_val_id_bc	= 0x1f<<24,
+
+	/* gdfifocfg */
+	Gdfifocfg	= 0xffff<<0,
+	Epinfobase	= 0xffff<<16,
+
+	/* adpctl */
+	Prb_dschg	= 0x3<<0,
+	Prb_delta	= 0x3<<2,
+	Prb_per		= 0x3<<4,
+	Rtim		= 0x7ff<<6,
+	Enaprb		= 1<<17,
+	Enasns		= 1<<18,
+	Adpres		= 1<<19,
+	Adpen		= 1<<20,
+	Adp_prb_int	= 1<<21,
+	Adp_sns_int	= 1<<22,
+	Adp_tmout_int	= 1<<23,
+	Adp_prb_int_msk	= 1<<24,
+	Adp_sns_int_msk	= 1<<25,
+	Adp_tmout_int_msk= 1<<26,
+	Ar		= 0x3<<27,
+
+	/* hcfg */
+	Fslspclksel	= 0x3<<0,
+		HCFG_30_60_MHZ	= 0<<0,
+		HCFG_48_MHZ	= 1<<0,
+		HCFG_6_MHZ	= 2<<0,
+	Fslssupp	= 1<<2,
+	Ena32khzs	= 1<<7,
+	Resvalid	= 0xff<<8,
+	Descdma		= 1<<23,
+	Frlisten	= 0x3<<24,
+	Modechtimen	= 1<<31,
+
+	/* hfir */
+	Frint		= 0xffff<<0,
+	Hfirrldctrl	= 1<<16,
+
+	/* hfnum */
+	Frnum		= 0xffff<<0,
+		MAX_FRNUM 	= 0x3FFF<<0,
+	Frrem		= 0xffff<<16,
+
+	/* hptxsts */
+	Ptxfspcavail	= 0xffff<<0,
+	Ptxqspcavail	= 0xff<<16,
+	Ptxqtop_terminate= 1<<24,
+	Ptxqtop_token	= 0x3<<25,
+	Ptxqtop_chnum	= 0xf<<27,
+	Ptxqtop_odd	= 1<<31,
+
+	/* haint, haintmsk */
+#define CHANINT(n)	(1<<(n))
+
+	/* hport0 */
+	Prtconnsts	= 1<<0,		/* connect status (RO) */
+	Prtconndet	= 1<<1,		/* connect detected R/W1C) */
+	Prtena		= 1<<2,		/* enable (R/W1C) */
+	Prtenchng	= 1<<3,		/* enable/disable change (R/W1C) */
+	Prtovrcurract	= 1<<4,		/* overcurrent active (RO) */
+	Prtovrcurrchng	= 1<<5,		/* overcurrent change (R/W1C) */
+	Prtres		= 1<<6,		/* resume */
+	Prtsusp		= 1<<7,		/* suspend */
+	Prtrst		= 1<<8,		/* reset */
+	Prtlnsts	= 0x3<<10,	/* line state {D+,D-} (RO) */
+	Prtpwr		= 1<<12,	/* power on */
+	Prttstctl	= 0xf<<13,	/* test */
+	Prtspd		= 0x3<<17,	/* speed (RO) */
+		HIGHSPEED	= 0<<17,
+		FULLSPEED	= 1<<17,
+		LOWSPEED	= 2<<17,
+
+	/* hcchar */
+	Mps		= 0x7ff<<0,	/* endpoint maximum packet size */
+	Epnum		= 0xf<<11,	/* endpoint number */
+		OEpnum		= 11,
+	Epdir		= 1<<15,	/* endpoint direction */
+		Epout		= 0<<15,
+		Epin		= 1<<15,
+	Lspddev		= 1<<17,	/* device is lowspeed */
+	Eptype		= 0x3<<18,	/* endpoint type */
+		Epctl		= 0<<18,
+		Episo		= 1<<18,
+		Epbulk		= 2<<18,
+		Epintr		= 3<<18,
+	Multicnt	= 0x3<<20,	/* transactions per μframe or retries */
+					/* per periodic split */
+		OMulticnt	= 20,
+	Devaddr		= 0x7f<<22,	/* device address */
+		ODevaddr	= 22,
+	Oddfrm		= 1<<29,	/* xfer in odd frame (iso/interrupt) */
+	Chdis		= 1<<30,	/* channel disable (write 1 only) */
+	Chen		= 1<<31,	/* channel enable (write 1 only) */
+
+	/* hcsplt */
+	Prtaddr		= 0x7f<<0,	/* port address of recipient */
+					/* transaction translator */
+	Hubaddr		= 0x7f<<7,	/* dev address of transaction */
+					/* translator's hub */
+		OHubaddr	= 7,
+	Xactpos		= 0x3<<14,	/* payload's position within transaction */
+		POS_MID		= 0<<14,		
+		POS_END		= 1<<14,
+		POS_BEGIN	= 2<<14,
+		POS_ALL		= 3<<14, /* all of data (<= 188 bytes) */
+	Compsplt	= 1<<16,	/* do complete split */
+	Spltena		= 1<<31,	/* channel enabled to do splits */
+
+	/* hcint, hcintmsk */
+	Xfercomp	= 1<<0,		/* transfer completed without error */
+	Chhltd		= 1<<1,		/* channel halted */
+	Ahberr		= 1<<2,		/* AHB dma error */
+	Stall		= 1<<3,
+	Nak		= 1<<4,
+	Ack		= 1<<5,
+	Nyet		= 1<<6,
+	Xacterr		= 1<<7,	/* transaction error (crc, t/o, bit stuff, eop) */
+	Bblerr		= 1<<8,
+	Frmovrun	= 1<<9,
+	Datatglerr	= 1<<10,
+	Bna		= 1<<11,
+	Xcs_xact	= 1<<12,
+	Frm_list_roll	= 1<<13,
+
+	/* hctsiz */
+	Xfersize	= 0x7ffff<<0,	/* expected total bytes */
+	Pktcnt		= 0x3ff<<19,	/* expected number of packets */
+		OPktcnt		= 19,
+	Pid		= 0x3<<29,	/* packet id for initial transaction */
+		DATA0		= 0<<29,
+		DATA1		= 2<<29,	/* sic */
+		DATA2		= 1<<29,	/* sic */
+		MDATA		= 3<<29,	/* (non-ctl ep) */
+		SETUP		= 3<<29,	/* (ctl ep) */
+	Dopng		= 1<<31,	/* do PING protocol */
+
+	/* pcgcctl */
+	Stoppclk		= 1<<0,
+	Gatehclk		= 1<<1,
+	Pwrclmp			= 1<<2,
+	Rstpdwnmodule		= 1<<3,
+	Enbl_sleep_gating	= 1<<5,
+	Phy_in_sleep		= 1<<6,
+	Deep_sleep		= 1<<7,
+	Resetaftsusp		= 1<<8,
+	Restoremode		= 1<<9,
+	Enbl_extnd_hiber	= 1<<10,
+	Extnd_hiber_pwrclmp	= 1<<11,
+	Extnd_hiber_switch	= 1<<12,
+	Ess_reg_restored	= 1<<13,
+	Prt_clk_sel		= 0x3<<14,
+	Port_power		= 1<<16,
+	Max_xcvrselect		= 0x3<<17,
+	Max_termsel		= 1<<19,
+	Mac_dev_addr		= 0x7f<<20,
+	P2hd_dev_enum_spd	= 0x3<<27,
+	P2hd_prt_spd		= 0x3<<29,
+	If_dev_mode		= 1<<31,
+};
--- /dev/null
+++ b/sys/src/9/bcm/emmc.c
@@ -1,0 +1,426 @@
+/*
+ * bcm2835 external mass media controller (mmc / sd host interface)
+ *
+ * Copyright © 2012 Richard Miller <[email protected]>
+ */
+
+#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 */
+	DTO		= 14,		/* data timeout exponent (guesswork) */
+
+	MMCSelect	= 7,		/* mmc/sd card select command */
+	Setbuswidth	= 6,		/* mmc/sd set bus width command */
+};
+
+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 */
+	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,
+};
+
+int cmdinfo[64] = {
+[0]  Ixchken,
+[2]  Resp136,
+[3]  Resp48 | Ixchken | Crcchken,
+[6]  Resp48 | Ixchken | Crcchken,
+[7]  Resp48busy | Ixchken | Crcchken,
+[8]  Resp48 | Ixchken | Crcchken,
+[9]  Resp136,
+[12] Resp48busy | Ixchken | Crcchken,
+[13] Resp48 | Ixchken | Crcchken,
+[16] Resp48,
+[17] Resp48 | Isdata | Card2host | Ixchken | Crcchken,
+[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 {
+	Rendez	r;
+	int	datadone;
+	int	fastclock;
+	ulong	extclk;
+};
+
+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);
+	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 int
+datadone(void*)
+{
+	return emmc.datadone;
+}
+
+static int
+emmcinit(void)
+{
+	u32int *r;
+	ulong clk;
+	char *s;
+
+	clk = getclkrate(ClkEmmc);
+	s = "";
+	if(clk == 0){
+		s = "Assuming ";
+		clk = Extfreq;
+	}
+	emmc.extclk = clk;
+	print("%seMMC external clock %lud Mhz\n", s, clk/1000000);
+	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)
+		;
+	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)
+{
+	u32int *r;
+	int i;
+
+	r = (u32int*)EMMCREGS;
+	WR(Control1, clkdiv(emmc.extclk / Initfreq - 1) | DTO << Datatoshift |
+		Clkgendiv | Clken | Clkintlen);
+	for(i = 0; i < 1000; i++){
+		delay(1);
+		if(r[Control1] & Clkstable)
+			break;
+	}
+	if(i == 1000)
+		print("SD clock won't initialise!\n");
+	WR(Irptmask, ~(Dtoerr|Cardintr));
+	intrenable(IRQmmc, mmcinterrupt, nil, 0, "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];
+	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]) != 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) != Ctoerr)
+			print("emmc: cmd %ux error intr %ux stat %ux\n", c, 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, Datadone|Err);
+		tsleep(&emmc.r, datadone, 0, 3000);
+		i = emmc.datadone;
+		emmc.datadone = 0;
+		WR(Irpten, 0);
+		if((i & Datadone) == 0)
+			print("emmcio: no Datadone after CMD%d\n", cmd);
+		if(i & Err)
+			print("emmcio: CMD%d error interrupt %ux\n",
+				cmd, r[Interrupt]);
+		WR(Interrupt, i);
+	}
+	/*
+	 * Once card is selected, use faster clock
+	 */
+	if(cmd == MMCSelect){
+		delay(10);
+		WR(Control1, clkdiv(emmc.extclk / SDfreq - 1) |
+			DTO << Datatoshift | Clkgendiv | Clken | Clkintlen);
+		for(i = 0; i < 1000; i++){
+			delay(1);
+			if(r[Control1] & Clkstable)
+				break;
+		}
+		delay(10);
+		emmc.fastclock = 1;
+	}
+	/*
+	 * If card bus width changes, change host bus width
+	 */
+	if(cmd == Setbuswidth)
+		switch(arg){
+		case 0:
+			WR(Control0, r[Control0] & ~Dwidth4);
+			break;
+		case 2:
+			WR(Control0, r[Control0] | Dwidth4);
+			break;
+		}
+	return 0;
+}
+
+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, Datadone|Err);
+	tsleep(&emmc.r, datadone, 0, 3000);
+	i = emmc.datadone;
+	emmc.datadone = 0;
+	WR(Irpten, 0);
+	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];
+	r[Interrupt] = i & (Datadone|Err);
+	emmc.datadone = i;
+	wakeup(&emmc.r);
+}
+
+SDio sdio = {
+	"emmc",
+	emmcinit,
+	emmcenable,
+	emmcinquiry,
+	emmccmd,
+	emmciosetup,
+	emmcio,
+};
--- /dev/null
+++ b/sys/src/9/bcm/fns.h
@@ -1,0 +1,118 @@
+#include "../port/portfns.h"
+
+Dirtab*	addarchfile(char*, int, long(*)(Chan*, void*, long, vlong), 
+	long(*)(Chan*, void*, long, vlong));
+extern void archreboot(void);
+extern void archreset(void);
+extern void armtimerset(int);
+extern void cachedwbinv(void);
+extern void cachedwbse(void*, int);
+extern void cachedwbinvse(void*, int);
+extern void cacheiinv(void);
+extern void cacheuwbinv(void);
+extern uintptr cankaddr(uintptr pa);
+extern int cas32(void*, u32int, u32int);
+extern void checkmmu(uintptr, uintptr);
+extern void clockinit(void);
+extern void clockshutdown(void);
+extern int cmpswap(long*, long, long);
+extern void coherence(void);
+extern ulong cprd(int cp, int op1, int crn, int crm, int op2);
+extern ulong cprdsc(int op1, int crn, int crm, int op2);
+extern void cpuidprint(void);
+extern void cpwr(int cp, int op1, int crn, int crm, int op2, ulong val);
+extern void cpwrsc(int op1, int crn, int crm, int op2, ulong val);
+#define cycles(ip) *(ip) = lcycles()
+extern void dmastart(int, int, int, void*, void*, int);
+extern int dmawait(int);
+extern int fbblank(int);
+extern void* fbinit(int, int*, int*, int*);
+extern u32int farget(void);
+extern void fpon(void);
+extern ulong fprd(int fpreg);
+extern void fprestreg(int fpreg, uvlong val);
+extern void fpsave(FPsave *);
+extern ulong fpsavereg(int fpreg, uvlong *fpp);
+extern void fpwr(int fpreg, ulong val);
+extern u32int fsrget(void);
+extern ulong getclkrate(int);
+extern char* getconf(char*);
+extern char *getethermac(void);
+extern uint getfirmware(void);
+extern int getpower(int);
+extern void getramsize(Confmem*);
+extern u32int ifsrget(void);
+extern void irqenable(int, void (*)(Ureg*, void*), void*);
+#define intrenable(i, f, a, b, n) irqenable((i), (f), (a))
+extern void intrsoff(void);
+extern int isaconfig(char*, int, ISAConf*);
+extern void links(void);
+extern void mmuinit(void);
+extern void mmuinit1(void);
+extern void mmuinvalidate(void);
+extern void mmuinvalidateaddr(u32int);
+extern uintptr mmukmap(uintptr, uintptr, usize);
+extern void okay(int);
+extern void procrestore(Proc *);
+extern void procsave(Proc*);
+extern void procfork(Proc*);
+extern void procsetup(Proc*);
+extern void screeninit(void);
+#define sdfree(p) free(p)
+#define sdmalloc(n)	mallocalign(n, CACHELINESZ, 0, 0)
+extern void setpower(int, int);
+extern void setr13(int, u32int*);
+extern int splfhi(void);
+extern int splflo(void);
+extern void swcursorinit(void);
+extern int tas(void *);
+extern void touser(uintptr);
+extern void trapinit(void);
+extern void uartconsinit(void);
+extern int userureg(Ureg*);
+extern void vectors(void);
+extern void vtable(void);
+
+/*
+ * floating point emulation
+ */
+extern int fpiarm(Ureg*);
+extern int fpudevprocio(Proc*, void*, long, uintptr, int);
+extern void fpuinit(void);
+extern void fpunoted(void);
+extern void fpunotify(Ureg*);
+extern void fpuprocrestore(Proc*);
+extern void fpuprocsave(Proc*);
+extern void fpusysprocsetup(Proc*);
+extern void fpusysrfork(Ureg*);
+extern void fpusysrforkchild(Proc*, Ureg*, Proc*);
+extern int fpuemu(Ureg*);
+/*
+ * Things called from port.
+ */
+extern void delay(int);				/* only scheddump() */
+extern int islo(void);
+extern void microdelay(int);			/* only edf.c */
+extern void evenaddr(uintptr);
+extern void idlehands(void);
+extern void setkernur(Ureg*, Proc*);		/* only devproc.c */
+extern void* sysexecregs(uintptr, ulong, int);
+extern void sysprocsetup(Proc*);
+
+extern void kexit(Ureg*);
+
+#define	getpgcolor(a)	0
+#define	kmapinval()
+#define countpagerefs(a, b)
+
+#define PTR2UINT(p)	((uintptr)(p))
+#define UINT2PTR(i)	((void*)(i))
+
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+
+#define KADDR(pa)	UINT2PTR(KZERO    | ((uintptr)(pa) & ~KSEGM))
+#define PADDR(va)	PTR2UINT(PHYSDRAM | ((uintptr)(va) & ~KSEGM))
+#define DMAADDR(va)	PTR2UINT(BUSDRAM  | ((uintptr)(va) & ~KSEGM))
+#define DMAIO(va)	PTR2UINT(BUSIO    | ((uintptr)(va) & ~VIRTIO))
+
+#define MASK(v)	((1UL << (v)) - 1)	/* mask `v' bits wide */
--- /dev/null
+++ b/sys/src/9/bcm/fpi.c
@@ -1,0 +1,1 @@
+#include "../omap/fpi.c"
--- /dev/null
+++ b/sys/src/9/bcm/fpi.h
@@ -1,0 +1,1 @@
+#include "../omap/fpi.h"
--- /dev/null
+++ b/sys/src/9/bcm/fpiarm.c
@@ -1,0 +1,506 @@
+/*
+ * this doesn't attempt to implement ARM floating-point properties
+ * that aren't visible in the Inferno environment.
+ * all arithmetic is done in double precision.
+ * the FP trap status isn't updated.
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+#include	"ureg.h"
+
+#include	"arm.h"
+#include	"fpi.h"
+
+#define ARM7500			/* emulate old pre-VFP opcodes */
+
+/* undef this if correct kernel r13 isn't in Ureg;
+ * check calculation in fpiarm below
+ */
+
+#define	REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)]))
+#ifdef ARM7500
+#define	FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7])
+#else
+#define	FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpregs - 1)])
+#endif
+
+typedef struct FP2 FP2;
+typedef struct FP1 FP1;
+
+struct FP2 {
+	char*	name;
+	void	(*f)(Internal, Internal, Internal*);
+};
+
+struct FP1 {
+	char*	name;
+	void	(*f)(Internal*, Internal*);
+};
+
+enum {
+	N = 1<<31,
+	Z = 1<<30,
+	C = 1<<29,
+	V = 1<<28,
+	REGPC = 15,
+};
+
+enum {
+	fpemudebug = 0,
+};
+
+#undef OFR
+#define	OFR(X)	((ulong)&((Ureg*)0)->X)
+
+static	int	roff[] = {
+	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
+	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
+	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
+	OFR(r12), OFR(r13), OFR(r14), OFR(pc),
+};
+
+static Internal fpconst[8] = {		/* indexed by op&7 (ARM 7500 FPA) */
+	/* s, e, l, h */
+	{0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
+	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1.0 */
+	{0, 0x400, 0x00000000, 0x08000000},	/* 2.0 */
+	{0, 0x400, 0x00000000, 0x0C000000},	/* 3.0 */
+	{0, 0x401, 0x00000000, 0x08000000},	/* 4.0 */
+	{0, 0x401, 0x00000000, 0x0A000000},	/* 5.0 */
+	{0, 0x3FE, 0x00000000, 0x08000000},	/* 0.5 */
+	{0, 0x402, 0x00000000, 0x0A000000},	/* 10.0 */
+};
+
+/*
+ * arm binary operations
+ */
+
+static void
+fadd(Internal m, Internal n, Internal *d)
+{
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsub(Internal m, Internal n, Internal *d)
+{
+	m.s ^= 1;
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsubr(Internal m, Internal n, Internal *d)
+{
+	n.s ^= 1;
+	(n.s == m.s? fpiadd: fpisub)(&n, &m, d);
+}
+
+static void
+fmul(Internal m, Internal n, Internal *d)
+{
+	fpimul(&m, &n, d);
+}
+
+static void
+fdiv(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&m, &n, d);
+}
+
+static void
+fdivr(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&n, &m, d);
+}
+
+/*
+ * arm unary operations
+ */
+
+static void
+fmov(Internal *m, Internal *d)
+{
+	*d = *m;
+}
+
+static void
+fmovn(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s ^= 1;
+}
+
+static void
+fabsf(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s = 0;
+}
+
+static void
+frnd(Internal *m, Internal *d)
+{
+	short e;
+
+	(m->s? fsub: fadd)(fpconst[6], *m, d);
+	if(IsWeird(d))
+		return;
+	fpiround(d);
+	e = (d->e - ExpBias) + 1;
+	if(e <= 0)
+		SetZero(d);
+	else if(e > FractBits){
+		if(e < 2*FractBits)
+			d->l &= ~((1<<(2*FractBits - e))-1);
+	}else{
+		d->l = 0;
+		if(e < FractBits)
+			d->h &= ~((1<<(FractBits-e))-1);
+	}
+}
+
+/*
+ * ARM 7500 FPA opcodes
+ */
+
+static	FP1	optab1[16] = {	/* Fd := OP Fm */
+[0]	{"MOVF",	fmov},
+[1]	{"NEGF",	fmovn},
+[2]	{"ABSF",	fabsf},
+[3]	{"RNDF",	frnd},
+[4]	{"SQTF",	/*fsqt*/0},
+/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
+/* URD and NRM aren't implemented */
+};
+
+static	FP2	optab2[16] = {	/* Fd := Fn OP Fm */
+[0]	{"ADDF",	fadd},
+[1]	{"MULF",	fmul},
+[2]	{"SUBF",	fsub},
+[3]	{"RSUBF",	fsubr},
+[4]	{"DIVF",	fdiv},
+[5]	{"RDIVF",	fdivr},
+/* POW, RPW deprecated */
+[8]	{"REMF",	/*frem*/0},
+[9]	{"FMF",	fmul},	/* fast multiply */
+[10]	{"FDV",	fdiv},	/* fast divide */
+[11]	{"FRD",	fdivr},	/* fast reverse divide */
+/* POL deprecated */
+};
+
+static ulong
+fcmp(Internal *n, Internal *m)
+{
+	int i;
+	Internal rm, rn;
+
+	if(IsWeird(m) || IsWeird(n)){
+		/* BUG: should trap if not masked */
+		return V|C;
+	}
+	rn = *n;
+	rm = *m;
+	fpiround(&rn);
+	fpiround(&rm);
+	i = fpicmp(&rn, &rm);
+	if(i > 0)
+		return C;
+	else if(i == 0)
+		return C|Z;
+	else
+		return N;
+}
+
+static void
+fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
+{
+	void *mem;
+
+	mem = (void*)ea;
+	(*f)(&FR(ufp, d), mem);
+	if(fpemudebug)
+		print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
+}
+
+static void
+fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
+{
+	Internal tmp;
+	void *mem;
+
+	mem = (void*)ea;
+	tmp = FR(ufp, s);
+	if(fpemudebug)
+		print("MOV%c	F%d,#%lux\n", n==8? 'D': 'F', s, ea);
+	(*f)(mem, &tmp);
+}
+
+static int
+condok(int cc, int c)
+{
+	switch(c){
+	case 0:	/* Z set */
+		return cc&Z;
+	case 1:	/* Z clear */
+		return (cc&Z) == 0;
+	case 2:	/* C set */
+		return cc&C;
+	case 3:	/* C clear */
+		return (cc&C) == 0;
+	case 4:	/* N set */
+		return cc&N;
+	case 5:	/* N clear */
+		return (cc&N) == 0;
+	case 6:	/* V set */
+		return cc&V;
+	case 7:	/* V clear */
+		return (cc&V) == 0;
+	case 8:	/* C set and Z clear */
+		return cc&C && (cc&Z) == 0;
+	case 9:	/* C clear or Z set */
+		return (cc&C) == 0 || cc&Z;
+	case 10:	/* N set and V set, or N clear and V clear */
+		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
+	case 11:	/* N set and V clear, or N clear and V set */
+		return (cc&(N|V))==N || (cc&(N|V))==V;
+	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
+		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
+	case 13:	/* Z set, or N set and V clear or N clear and V set */
+		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
+	case 14:	/* always */
+		return 1;
+	case 15:	/* never (reserved) */
+		return 0;
+	}
+	return 0;	/* not reached */
+}
+
+static void
+unimp(ulong pc, ulong op)
+{
+	char buf[60];
+
+	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
+	if(fpemudebug)
+		print("FPE: %s\n", buf);
+	error(buf);
+	/* no return */
+}
+
+static void
+fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
+{
+	int rn, rd, tag, o;
+	long off;
+	ulong ea;
+	Internal tmp, *fm, *fn;
+
+	/* note: would update fault status here if we noted numeric exceptions */
+
+	/*
+	 * LDF, STF; 10.1.1
+	 */
+	if(((op>>25)&7) == 6){
+		if(op & (1<<22))
+			unimp(pc, op);	/* packed or extended */
+		rn = (op>>16)&0xF;
+		off = (op&0xFF)<<2;
+		if((op & (1<<23)) == 0)
+			off = -off;
+		ea = REG(ur, rn);
+		if(rn == REGPC)
+			ea += 8;
+		if(op & (1<<24))
+			ea += off;
+		rd = (op>>12)&7;
+		if(op & (1<<20)){
+			if(op & (1<<15))
+				fld(fpid2i, rd, ea, 8, ufp);
+			else
+				fld(fpis2i, rd, ea, 4, ufp);
+		}else{
+			if(op & (1<<15))
+				fst(fpii2d, ea, rd, 8, ufp);
+			else
+				fst(fpii2s, ea, rd, 4, ufp);
+		}
+		if((op & (1<<24)) == 0)
+			ea += off;
+		if(op & (1<<21))
+			REG(ur, rn) = ea;
+		return;
+	}
+
+	/*
+	 * CPRT/transfer, 10.3
+	 */
+	if(op & (1<<4)){
+		rd = (op>>12) & 0xF;
+
+		/*
+		 * compare, 10.3.1
+		 */
+		if(rd == 15 && op & (1<<20)){
+			rn = (op>>16)&7;
+			fn = &FR(ufp, rn);
+			if(op & (1<<3)){
+				fm = &fpconst[op&7];
+				if(fpemudebug)
+					tag = 'C';
+			}else{
+				fm = &FR(ufp, op&7);
+				if(fpemudebug)
+					tag = 'F';
+			}
+			switch((op>>21)&7){
+			default:
+				unimp(pc, op);
+			case 4:	/* CMF: Fn :: Fm */
+			case 6:	/* CMFE: Fn :: Fm (with exception) */
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, fm);
+				break;
+			case 5:	/* CNF: Fn :: -Fm */
+			case 7:	/* CNFE: Fn :: -Fm (with exception) */
+				tmp = *fm;
+				tmp.s ^= 1;
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, &tmp);
+				break;
+			}
+			if(fpemudebug)
+				print("CMPF	%c%d,F%ld =%#lux\n",
+					tag, rn, op&7, ur->psr>>28);
+			return;
+		}
+
+		/*
+		 * other transfer, 10.3
+		 */
+		switch((op>>20)&0xF){
+		default:
+			unimp(pc, op);
+		case 0:	/* FLT */
+			rn = (op>>16) & 7;
+			fpiw2i(&FR(ufp, rn), &REG(ur, rd));
+			if(fpemudebug)
+				print("MOVW[FD]	R%d, F%d\n", rd, rn);
+			break;
+		case 1:	/* FIX */
+			if(op & (1<<3))
+				unimp(pc, op);
+			rn = op & 7;
+			tmp = FR(ufp, rn);
+			fpii2w(&REG(ur, rd), &tmp);
+			if(fpemudebug)
+				print("MOV[FD]W	F%d, R%d =%ld\n", rn, rd, REG(ur, rd));
+			break;
+		case 2:	/* FPSR := Rd */
+			ufp->status = REG(ur, rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPSR\n", rd);
+			break;
+		case 3:	/* Rd := FPSR */
+			REG(ur, rd) = ufp->status;
+			if(fpemudebug)
+				print("MOVW	FPSR, R%d\n", rd);
+			break;
+		case 4:	/* FPCR := Rd */
+			ufp->control = REG(ur, rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPCR\n", rd);
+			break;
+		case 5:	/* Rd := FPCR */
+			REG(ur, rd) = ufp->control;
+			if(fpemudebug)
+				print("MOVW	FPCR, R%d\n", rd);
+			break;
+		}
+		return;
+	}
+
+	/*
+	 * arithmetic
+	 */
+
+	if(op & (1<<3)){	/* constant */
+		fm = &fpconst[op&7];
+		if(fpemudebug)
+			tag = 'C';
+	}else{
+		fm = &FR(ufp, op&7);
+		if(fpemudebug)
+			tag = 'F';
+	}
+	rd = (op>>12)&7;
+	o = (op>>20)&0xF;
+	if(op & (1<<15)){	/* monadic */
+		FP1 *fp;
+		fp = &optab1[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		if(fpemudebug)
+			print("%s	%c%ld,F%d\n", fp->name, tag, op&7, rd);
+		(*fp->f)(fm, &FR(ufp, rd));
+	} else {
+		FP2 *fp;
+		fp = &optab2[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		rn = (op>>16)&7;
+		if(fpemudebug)
+			print("%s	%c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
+		(*fp->f)(*fm, FR(ufp, rn), &FR(ufp, rd));
+	}
+}
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpiarm(Ureg *ur)
+{
+	ulong op, o, cp;
+	FPsave *ufp;
+	int n;
+
+	if(up == nil)
+		panic("fpiarm not in a process");
+	ufp = &up->fpsave;
+	/*
+	 * because all the emulated fp state is in the proc structure,
+	 * it need not be saved/restored
+	 */
+	switch(up->fpstate){
+	case FPactive:
+	case FPinactive:
+		error("illegal instruction: emulated fpu opcode in VFP mode");
+	case FPinit:
+		assert(sizeof(Internal) <= sizeof(ufp->regs[0]));
+		up->fpstate = FPemu;
+		ufp->control = 0;
+		ufp->status = (0x01<<28)|(1<<12); /* sw emulation, alt. C flag */
+		for(n = 0; n < 8; n++)
+			FR(ufp, n) = fpconst[0];
+	}
+	for(n=0; ;n++){
+		validaddr(ur->pc, 4, 0);
+		op = *(ulong*)(ur->pc);
+		if(fpemudebug)
+			print("%#lux: %#8.8lux ", ur->pc, op);
+		o = (op>>24) & 0xF;
+		cp = (op>>8) & 0xF;
+		if(!ISFPAOP(cp, o))
+			break;
+		if(condok(ur->psr, op>>28))
+			fpemu(ur->pc, op, ur, ufp);
+		ur->pc += 4;		/* pretend cpu executed the instr */
+	}
+	if(fpemudebug)
+		print("\n");
+	return n;
+}
--- /dev/null
+++ b/sys/src/9/bcm/fpimem.c
@@ -1,0 +1,1 @@
+#include "../omap/fpimem.c"
--- /dev/null
+++ b/sys/src/9/bcm/init9.s
@@ -1,0 +1,1 @@
+#include "../omap/init9.s"
--- /dev/null
+++ b/sys/src/9/bcm/io.h
@@ -1,0 +1,46 @@
+enum {
+	IRQtimer0	= 0,
+	IRQtimer1	= 1,
+	IRQtimer2	= 2,
+	IRQtimer3	= 3,
+	IRQclock	= IRQtimer3,
+	IRQusb		= 9,
+	IRQdma0		= 16,
+#define IRQDMA(chan)	(IRQdma0+(chan))
+	IRQaux		= 29,
+	IRQmmc		= 62,
+
+	IRQbasic	= 64,
+	IRQtimerArm	= IRQbasic + 0,
+
+	IRQfiq		= IRQusb,	/* only one source can be FIQ */
+
+	DmaD2M		= 0,		/* device to memory */
+	DmaM2D		= 1,		/* memory to device */
+	DmaM2M		= 2,		/* memory to memory */
+
+	DmaChanEmmc	= 4,		/* can only use 2-5, maybe 0 */
+	DmaDevEmmc	= 11,
+
+	PowerSd		= 0,
+	PowerUart0,
+	PowerUart1,
+	PowerUsb,
+	PowerI2c0,
+	PowerI2c1,
+	PowerI2c2,
+	PowerSpi,
+	PowerCcp2tx,
+
+	ClkEmmc		= 1,
+	ClkUart,
+	ClkArm,
+	ClkCore,
+	ClkV3d,
+	ClkH264,
+	ClkIsp,
+	ClkSdram,
+	ClkPixel,
+	ClkPwm,
+};
+#define BUSUNKNOWN	(-1)
--- /dev/null
+++ b/sys/src/9/bcm/l.s
@@ -1,0 +1,274 @@
+/*
+ * Broadcom bcm2835 SoC, as used in Raspberry Pi
+ * arm1176jzf-s processor (armv6)
+ */
+
+#include "arm.s"
+
+TEXT _start(SB), 1, $-4
+	/*
+	 * load physical base for SB addressing while mmu is off
+	 * keep a handy zero in R0 until first function call
+	 */
+	MOVW	$setR12(SB), R12
+	SUB	$KZERO, R12
+	ADD	$PHYSDRAM, R12
+	MOVW	$0, R0
+
+	/*
+	 * SVC mode, interrupts disabled
+	 */
+	MOVW	$(PsrDirq|PsrDfiq|PsrMsvc), R1
+	MOVW	R1, CPSR
+
+	/*
+	 * disable the mmu and L1 caches
+	 * invalidate caches and tlb
+	 */
+	MRC	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+	BIC	$(CpCdcache|CpCicache|CpCpredict|CpCmmu), R1
+	MCR	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvu), CpCACHEall
+	MCR	CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+	ISB
+
+	/*
+	 * clear mach and page tables
+	 */
+	MOVW	$PADDR(MACHADDR), R1
+	MOVW	$PADDR(KTZERO), R2
+_ramZ:
+	MOVW	R0, (R1)
+	ADD	$4, R1
+	CMP	R1, R2
+	BNE	_ramZ
+
+	/*
+	 * start stack at top of mach (physical addr)
+	 * set up page tables for kernel
+	 */
+	MOVW	$PADDR(MACHADDR+MACHSIZE-4), R13
+	BL	,mmuinit(SB)
+
+	/*
+	 * set up domain access control and page table base
+	 */
+	MOVW	$Client, R1
+	MCR	CpSC, 0, R1, C(CpDAC), C(0)
+	MOVW	$PADDR(L1), R1
+	MCR	CpSC, 0, R1, C(CpTTB), C(0)
+
+	/*
+	 * enable caches, mmu, and high vectors
+	 */
+	MRC	CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
+	ORR	$(CpChv|CpCdcache|CpCicache|CpCmmu), R0
+	MCR	CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
+	ISB
+
+	/*
+	 * switch SB, SP, and PC into KZERO space
+	 */
+	MOVW	$setR12(SB), R12
+	MOVW	$(MACHADDR+MACHSIZE-4), R13
+	MOVW	$_startpg(SB), R15
+
+TEXT _startpg(SB), 1, $-4
+
+	/*
+	 * enable cycle counter
+	 */
+	MOVW	$1, R1
+	MCR	CpSC, 0, R1, C(CpSPM), C(CpSPMperf), CpSPMctl
+
+	/*
+	 * call main and loop forever if it returns
+	 */
+	BL	,main(SB)
+	B	,0(PC)
+
+	BL	_div(SB)		/* hack to load _div, etc. */
+
+TEXT fsrget(SB), 1, $-4				/* data fault status */
+	MRC	CpSC, 0, R0, C(CpFSR), C(0), CpFSRdata
+	RET
+
+TEXT ifsrget(SB), 1, $-4			/* instruction fault status */
+	MRC	CpSC, 0, R0, C(CpFSR), C(0), CpFSRinst
+	RET
+
+TEXT farget(SB), 1, $-4				/* fault address */
+	MRC	CpSC, 0, R0, C(CpFAR), C(0x0)
+	RET
+
+TEXT lcycles(SB), 1, $-4
+	MRC	CpSC, 0, R0, C(CpSPM), C(CpSPMperf), CpSPMcyc
+	RET
+
+TEXT splhi(SB), 1, $-4
+	MOVW	$(MACHADDR+4), R2		/* save caller pc in Mach */
+	MOVW	R14, 0(R2)
+
+	MOVW	CPSR, R0			/* turn off irqs (but not fiqs) */
+	ORR	$(PsrDirq), R0, R1
+	MOVW	R1, CPSR
+	RET
+
+TEXT splfhi(SB), 1, $-4
+	MOVW	$(MACHADDR+4), R2		/* save caller pc in Mach */
+	MOVW	R14, 0(R2)
+
+	MOVW	CPSR, R0			/* turn off irqs and fiqs */
+	ORR	$(PsrDirq|PsrDfiq), R0, R1
+	MOVW	R1, CPSR
+	RET
+
+TEXT splflo(SB), 1, $-4
+	MOVW	CPSR, R0			/* turn on fiqs */
+	BIC	$(PsrDfiq), R0, R1
+	MOVW	R1, CPSR
+	RET
+
+TEXT spllo(SB), 1, $-4
+	MOVW	CPSR, R0			/* turn on irqs and fiqs */
+	BIC	$(PsrDirq|PsrDfiq), R0, R1
+	MOVW	R1, CPSR
+	RET
+
+TEXT splx(SB), 1, $-4
+	MOVW	$(MACHADDR+0x04), R2		/* save caller pc in Mach */
+	MOVW	R14, 0(R2)
+
+	MOVW	R0, R1				/* reset interrupt level */
+	MOVW	CPSR, R0
+	MOVW	R1, CPSR
+	RET
+
+TEXT spldone(SB), 1, $0				/* end marker for devkprof.c */
+	RET
+
+TEXT islo(SB), 1, $-4
+	MOVW	CPSR, R0
+	AND	$(PsrDirq), R0
+	EOR	$(PsrDirq), R0
+	RET
+
+TEXT	tas(SB), $-4
+TEXT	_tas(SB), $-4
+	MOVW	R0,R1
+	MOVW	$1,R0
+	SWPW	R0,(R1)			/* fix: deprecated in armv6 */
+	RET
+
+TEXT setlabel(SB), 1, $-4
+	MOVW	R13, 0(R0)		/* sp */
+	MOVW	R14, 4(R0)		/* pc */
+	MOVW	$0, R0
+	RET
+
+TEXT gotolabel(SB), 1, $-4
+	MOVW	0(R0), R13		/* sp */
+	MOVW	4(R0), R14		/* pc */
+	MOVW	$1, R0
+	RET
+
+TEXT getcallerpc(SB), 1, $-4
+	MOVW	0(R13), R0
+	RET
+
+TEXT idlehands(SB), $-4
+	BARRIERS
+	MOVW	CPSR, R3
+	BIC	$(PsrDirq|PsrDfiq), R3, R1		/* spllo */
+	MOVW	R1, CPSR
+
+	MOVW	$0, R0				/* wait for interrupt */
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEintr), CpCACHEwait
+	ISB
+
+	MOVW	R3, CPSR			/* splx */
+	RET
+
+
+TEXT coherence(SB), $-4
+	BARRIERS
+	RET
+
+/*
+ * invalidate tlb
+ */
+TEXT mmuinvalidate(SB), 1, $-4
+	MOVW	$0, R0
+	MCR	CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+	BARRIERS
+	RET
+
+/*
+ * mmuinvalidateaddr(va)
+ *   invalidate tlb entry for virtual page address va, ASID 0
+ */
+TEXT mmuinvalidateaddr(SB), 1, $-4
+	MCR	CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinvse
+	BARRIERS
+	RET
+
+/*
+ * drain write buffer
+ * writeback and invalidate data cache
+ */
+TEXT cachedwbinv(SB), 1, $-4
+	DSB
+	MOVW	$0, R0
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEall
+	RET
+
+/*
+ * cachedwbinvse(va, n)
+ *   drain write buffer
+ *   writeback and invalidate data cache range [va, va+n)
+ */
+TEXT cachedwbinvse(SB), 1, $-4
+	MOVW	R0, R1		/* DSB clears R0 */
+	DSB
+	MOVW	n+4(FP), R2
+	ADD	R1, R2
+	SUB	$1, R2
+	BIC	$(CACHELINESZ-1), R1
+	BIC	$(CACHELINESZ-1), R2
+	MCRR(CpSC, 0, 2, 1, CpCACHERANGEdwbi)
+	RET
+
+/*
+ * cachedwbse(va, n)
+ *   drain write buffer
+ *   writeback data cache range [va, va+n)
+ */
+TEXT cachedwbse(SB), 1, $-4
+	MOVW	R0, R1		/* DSB clears R0 */
+	DSB
+	MOVW	n+4(FP), R2
+	ADD	R1, R2
+	BIC	$(CACHELINESZ-1), R1
+	BIC	$(CACHELINESZ-1), R2
+	MCRR(CpSC, 0, 2, 1, CpCACHERANGEdwb)
+	RET
+
+/*
+ * drain write buffer and prefetch buffer
+ * writeback and invalidate data cache
+ * invalidate instruction cache
+ */
+TEXT cacheuwbinv(SB), 1, $-4
+	BARRIERS
+	MOVW	$0, R0
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEall
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
+	RET
+
+/*
+ * invalidate instruction cache
+ */
+TEXT cacheiinv(SB), 1, $-4
+	MOVW	$0, R0
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
+	RET
--- /dev/null
+++ b/sys/src/9/bcm/lexception.s
@@ -1,0 +1,197 @@
+/*
+ * arm exception handlers
+ */
+#include "arm.s"
+
+/*
+ *  exception vectors, copied by trapinit() to somewhere useful
+ */
+TEXT vectors(SB), 1, $-4
+	MOVW	0x18(R15), R15		/* reset */
+	MOVW	0x18(R15), R15		/* undefined instr. */
+	MOVW	0x18(R15), R15		/* SWI & SMC */
+	MOVW	0x18(R15), R15		/* prefetch abort */
+	MOVW	0x18(R15), R15		/* data abort */
+	MOVW	0x18(R15), R15		/* reserved */
+	MOVW	0x18(R15), R15		/* IRQ */
+	MOVW	0x18(R15), R15		/* FIQ */
+
+TEXT vtable(SB), 1, $-4
+	WORD	$_vsvc(SB)		/* reset, in svc mode already */
+	WORD	$_vund(SB)		/* undefined, switch to svc mode */
+	WORD	$_vsvc(SB)		/* swi, in svc mode already */
+	WORD	$_vpabt(SB)		/* prefetch abort, switch to svc mode */
+	WORD	$_vdabt(SB)		/* data abort, switch to svc mode */
+	WORD	$_vsvc(SB)		/* reserved */
+	WORD	$_virq(SB)		/* IRQ, switch to svc mode */
+	WORD	$_vfiq(SB)		/* FIQ, switch to svc mode */
+
+TEXT _vsvc(SB), 1, $-4			/* SWI */
+	MOVW.W	R14, -4(R13)		/* ureg->pc = interrupted PC */
+	MOVW	SPSR, R14		/* ureg->psr = SPSR */
+	MOVW.W	R14, -4(R13)		/* ... */
+	MOVW	$PsrMsvc, R14		/* ureg->type = PsrMsvc */
+	MOVW.W	R14, -4(R13)		/* ... */
+
+	/* avoid the ambiguity described in notes/movm.w. */
+	MOVM.DB.S [R0-R14], (R13)	/* save user level registers */
+	SUB	$(15*4), R13		/* r13 now points to ureg */
+
+	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
+
+//	MOVW	$(KSEG0+16*KiB-MACHSIZE), R10	/* m */
+	MOVW	$(MACHADDR), R10	/* m */
+	MOVW	8(R10), R9		/* up */
+
+	MOVW	R13, R0			/* first arg is pointer to ureg */
+	SUB	$8, R13			/* space for argument+link */
+
+	BL	syscall(SB)
+
+	ADD	$(8+4*15), R13		/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
+	ADD	$8, R13			/* pop past ureg->{type+psr} */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+TEXT _vund(SB), 1, $-4			/* undefined */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$PsrMund, R0
+	B	_vswitch
+
+TEXT _vpabt(SB), 1, $-4			/* prefetch abort */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$PsrMabt, R0		/* r0 = type */
+	B	_vswitch
+
+TEXT _vdabt(SB), 1, $-4			/* data abort */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$(PsrMabt+1), R0	/* r0 = type */
+	B	_vswitch
+
+TEXT _virq(SB), 1, $-4			/* IRQ */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$PsrMirq, R0		/* r0 = type */
+	B	_vswitch
+
+	/*
+	 *  come here with type in R0 and R13 pointing above saved [r0-r4].
+	 *  we'll switch to SVC mode and then call trap.
+	 */
+_vswitch:
+	MOVW	SPSR, R1		/* save SPSR for ureg */
+	MOVW	R14, R2			/* save interrupted pc for ureg */
+	MOVW	R13, R3			/* save pointer to where the original [R0-R4] are */
+
+	/*
+	 * switch processor to svc mode.  this switches the banked registers
+	 * (r13 [sp] and r14 [link]) to those of svc mode.
+	 */
+	MOVW	CPSR, R14
+	BIC	$PsrMask, R14
+	ORR	$(PsrDirq|PsrMsvc), R14
+	MOVW	R14, CPSR		/* switch! */
+
+	AND.S	$0xf, R1, R4		/* interrupted code kernel or user? */
+	BEQ	_userexcep
+
+	/* here for trap from SVC mode */
+	MOVM.DB.W [R0-R2], (R13)	/* set ureg->{type, psr, pc}; r13 points to ureg->type  */
+	MOVM.IA	  (R3), [R0-R4]		/* restore [R0-R4] from previous mode's stack */
+
+	/*
+	 * avoid the ambiguity described in notes/movm.w.
+	 * In order to get a predictable value in R13 after the stores,
+	 * separate the store-multiple from the stack-pointer adjustment.
+	 * We'll assume that the old value of R13 should be stored on the stack.
+	 */
+	/* save kernel level registers, at end r13 points to ureg */
+	MOVM.DB	[R0-R14], (R13)
+	SUB	$(15*4), R13		/* SP now points to saved R0 */
+
+	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
+
+	MOVW	R13, R0			/* first arg is pointer to ureg */
+	SUB	$(4*2), R13		/* space for argument+link (for debugger) */
+	MOVW	$0xdeaddead, R11	/* marker */
+
+	BL	trap(SB)
+
+	ADD	$(4*2+4*15), R13	/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+
+	MOVM.DB (R13), [R0-R14]		/* restore registers */
+
+	ADD	$(4*2), R13		/* pop past ureg->{type+psr} to pc */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+	/* here for trap from USER mode */
+_userexcep:
+	MOVM.DB.W [R0-R2], (R13)	/* set ureg->{type, psr, pc}; r13 points to ureg->type  */
+	MOVM.IA	  (R3), [R0-R4]		/* restore [R0-R4] from previous mode's stack */
+
+	/* avoid the ambiguity described in notes/movm.w. */
+	MOVM.DB.S [R0-R14], (R13)	/* save kernel level registers */
+	SUB	$(15*4), R13		/* r13 now points to ureg */
+
+	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
+
+//	MOVW	$(KSEG0+16*KiB-MACHSIZE), R10	/* m */
+	MOVW	$(MACHADDR), R10	/* m */
+	MOVW	8(R10), R9		/* up */
+
+	MOVW	R13, R0			/* first arg is pointer to ureg */
+	SUB	$(4*2), R13		/* space for argument+link (for debugger) */
+
+	BL	trap(SB)
+
+	ADD	$(4*2+4*15), R13	/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
+	ADD	$(4*2), R13		/* pop past ureg->{type+psr} */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+TEXT _vfiq(SB), 1, $-4			/* FIQ */
+	MOVW	$PsrMfiq, R8		/* trap type */
+	MOVW	SPSR, R9		/* interrupted psr */
+	MOVW	R14, R10		/* interrupted pc */
+	MOVM.DB.W [R8-R10], (R13)	/* save in ureg */
+	MOVM.DB.W.S [R0-R14], (R13)	/* save interrupted regs */
+	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
+	MOVW	$(MACHADDR), R10	/* m */
+	MOVW	8(R10), R9		/* up */
+	MOVW	R13, R0			/* first arg is pointer to ureg */
+	SUB	$(4*2), R13		/* space for argument+link (for debugger) */
+
+	BL	fiq(SB)
+
+	ADD	$(8+4*15), R13		/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
+	ADD	$8, R13			/* pop past ureg->{type+psr} */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+/*
+ *  set the stack value for the mode passed in R0
+ */
+TEXT setr13(SB), 1, $-4
+	MOVW	4(FP), R1
+
+	MOVW	CPSR, R2
+	BIC	$PsrMask, R2, R3
+	ORR	R0, R3
+	MOVW	R3, CPSR		/* switch to new mode */
+
+	MOVW	R13, R0			/* return old sp */
+	MOVW	R1, R13			/* install new one */
+
+	MOVW	R2, CPSR		/* switch back to old mode */
+	RET
--- /dev/null
+++ b/sys/src/9/bcm/lproc.s
@@ -1,0 +1,1 @@
+#include "../omap/lproc.s"
--- /dev/null
+++ b/sys/src/9/bcm/main.c
@@ -1,0 +1,597 @@
+#include "u.h"
+#include "tos.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "init.h"
+#include <pool.h>
+
+#include "reboot.h"
+
+/* Firmware compatibility */
+#define	Minfirmrev	326770
+#define	Minfirmdate	"22 Jul 2012"
+
+/*
+ * Where configuration info is left for the loaded programme.
+ */
+#define BOOTARGS	((char*)CONFADDR)
+#define	BOOTARGSLEN	(MACHADDR-CONFADDR)
+#define	MAXCONF		64
+#define MAXCONFLINE	160
+
+uintptr kseg0 = KZERO;
+Mach*	machaddr[MAXMACH];
+Conf	conf;
+ulong	memsize = 128*1024*1024;
+
+/*
+ * Option arguments from the command line.
+ * oargv[0] is the boot file.
+ */
+static int oargc;
+static char* oargv[20];
+static char oargb[128];
+static int oargblen;
+
+static uintptr sp;		/* XXX - must go - user stack of init proc */
+
+/* store plan9.ini contents here at least until we stash them in #ec */
+static char confname[MAXCONF][KNAMELEN];
+static char confval[MAXCONF][MAXCONFLINE];
+static int nconf;
+
+typedef struct Atag Atag;
+struct Atag {
+	u32int	size;	/* size of atag in words, including this header */
+	u32int	tag;	/* atag type */
+	union {
+		u32int	data[1];	/* actually [size-2] */
+		/* AtagMem */
+		struct {
+			u32int	size;
+			u32int	base;
+		} mem;
+		/* AtagCmdLine */
+		char	cmdline[1];	/* actually [4*(size-2)] */
+	};
+};
+
+enum {
+	AtagNone	= 0x00000000,
+	AtagCore	= 0x54410001,
+	AtagMem		= 0x54410002,
+	AtagCmdline	= 0x54410009,
+};
+
+static int
+findconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			return i;
+	return -1;
+}
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	i = findconf(name);
+	if(i >= 0)
+		return confval[i];
+	return nil;
+}
+
+void
+addconf(char *name, char *val)
+{
+	int i;
+
+	i = findconf(name);
+	if(i < 0){
+		if(val == nil || nconf >= MAXCONF)
+			return;
+		i = nconf++;
+		strecpy(confname[i], confname[i]+sizeof(confname[i]), name);
+	}
+	strecpy(confval[i], confval[i]+sizeof(confval[i]), val);
+}
+
+static void
+writeconf(void)
+{
+	char *p, *q;
+	int n;
+
+	p = getconfenv();
+
+	if(waserror()) {
+		free(p);
+		nexterror();
+	}
+
+	/* convert to name=value\n format */
+	for(q=p; *q; q++) {
+		q += strlen(q);
+		*q = '=';
+		q += strlen(q);
+		*q = '\n';
+	}
+	n = q - p + 1;
+	if(n >= BOOTARGSLEN)
+		error("kernel configuration too large");
+	memmove(BOOTARGS, p, n);
+	memset(BOOTARGS + n, '\n', BOOTARGSLEN - n);
+	poperror();
+	free(p);
+}
+
+static void
+plan9iniinit(char *s, int cmdline)
+{
+	char *toks[MAXCONF];
+	int i, c, n;
+	char *v;
+
+	if((c = *s) < ' ' || c >= 0x80)
+		return;
+	if(cmdline)
+		n = tokenize(s, toks, MAXCONF);
+	else
+		n = getfields(s, toks, MAXCONF, 1, "\n");
+	for(i = 0; i < n; i++){
+		if(toks[i][0] == '#')
+			continue;
+		v = strchr(toks[i], '=');
+		if(v == nil)
+			continue;
+		*v++ = '\0';
+		addconf(toks[i], v);
+	}
+}
+
+static void
+ataginit(Atag *a)
+{
+	int n;
+
+	if(a->tag != AtagCore){
+		plan9iniinit((char*)a, 0);
+		return;
+	}
+	while(a->tag != AtagNone){
+		switch(a->tag){
+		case AtagMem:
+			/* use only first bank */
+			if(conf.mem[0].limit == 0 && a->mem.size != 0){
+				memsize = a->mem.size;
+				conf.mem[0].base = a->mem.base;
+				conf.mem[0].limit = a->mem.base + memsize;
+			}
+			break;
+		case AtagCmdline:
+			n = (a->size * sizeof(u32int)) - offsetof(Atag, cmdline[0]);
+			if(a->cmdline + n < BOOTARGS + BOOTARGSLEN)
+				a->cmdline[n] = 0;
+			else
+				BOOTARGS[BOOTARGSLEN-1] = 0;
+			plan9iniinit(a->cmdline, 1);
+			break;
+		}
+		a = (Atag*)((u32int*)a + a->size);
+	}
+}
+
+void
+machinit(void)
+{
+	m->machno = 0;
+	machaddr[m->machno] = m;
+
+	m->ticks = 1;
+	m->perf.period = 1;
+
+	conf.nmach = 1;
+
+	active.machs = 1;
+	active.exiting = 0;
+
+	up = nil;
+}
+
+static void
+optionsinit(char* s)
+{
+	strecpy(oargb, oargb+sizeof(oargb), s);
+
+	oargblen = strlen(oargb);
+	oargc = tokenize(oargb, oargv, nelem(oargv)-1);
+	oargv[oargc] = nil;
+}
+
+void
+main(void)
+{
+	extern char edata[], end[];
+	uint rev;
+
+	okay(1);
+	m = (Mach*)MACHADDR;
+	memset(edata, 0, end - edata);	/* clear bss */
+	machinit();
+	mmuinit1();
+
+	optionsinit("/boot/boot boot");
+	quotefmtinstall();
+	
+	ataginit((Atag*)BOOTARGS);
+	confinit();		/* figures out amount of memory */
+	xinit();
+	uartconsinit();
+	screeninit();
+
+	print("\nPlan 9 from Bell Labs\n");
+	rev = getfirmware();
+	print("firmware: rev %d\n", rev);
+	if(rev < Minfirmrev){
+		print("Sorry, firmware (start.elf) must be at least rev %d (%s)\n",
+			Minfirmrev, Minfirmdate);
+		for(;;)
+			;
+	}
+	trapinit();
+	clockinit();
+	printinit();
+	timersinit();
+	if(conf.monitor)
+		swcursorinit();
+	cpuidprint();
+	archreset();
+
+	procinit0();
+	initseg();
+	links();
+	chandevreset();			/* most devices are discovered here */
+	pageinit();
+	swapinit();
+	userinit();
+	schedinit();
+	assert(0);			/* shouldn't have returned */
+}
+
+/*
+ *  starting place for first process
+ */
+void
+init0(void)
+{
+	int i;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+	coherence();
+	spllo();
+
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	up->slash = namec("#/", Atodir, 0, 0);
+	pathclose(up->slash->path);
+	up->slash->path = newpath("/");
+	up->dot = cclone(up->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		snprint(buf, sizeof(buf), "%s %s", "ARM", conffile);
+		ksetenv("terminal", buf, 0);
+		ksetenv("cputype", "arm", 0);
+		if(cpuserver)
+			ksetenv("service", "cpu", 0);
+		else
+			ksetenv("service", "terminal", 0);
+		snprint(buf, sizeof(buf), "-a %s", getethermac());
+		ksetenv("etherargs", buf, 0);
+
+		/* convert plan9.ini variables to #e and #ec */
+		for(i = 0; i < nconf; i++) {
+			ksetenv(confname[i], confval[i], 0);
+			ksetenv(confname[i], confval[i], 1);
+		}
+		poperror();
+	}
+	kproc("alarm", alarmkproc, 0);
+	touser(sp);
+	assert(0);			/* shouldn't have returned */
+}
+
+static void
+bootargs(uintptr base)
+{
+	int i;
+	ulong ssize;
+	char **av, *p;
+
+	/*
+	 * Push the boot args onto the stack.
+	 * The initial value of the user stack must be such
+	 * that the total used is larger than the maximum size
+	 * of the argument list checked in syscall.
+	 */
+	i = oargblen+1;
+	p = UINT2PTR(STACKALIGN(base + BY2PG - sizeof(Tos) - i));
+	memmove(p, oargb, i);
+
+	/*
+	 * Now push the argv pointers.
+	 * The code jumped to by touser in lproc.s expects arguments
+	 *	main(char* argv0, ...)
+	 * and calls
+	 * 	startboot("/boot/boot", &argv0)
+	 * not the usual (int argc, char* argv[])
+	 */
+	av = (char**)(p - (oargc+1)*sizeof(char*));
+	ssize = base + BY2PG - PTR2UINT(av);
+	for(i = 0; i < oargc; i++)
+		*av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BY2PG);
+	*av = nil;
+	sp = USTKTOP - ssize;
+}
+
+/*
+ *  create the first process
+ */
+void
+userinit(void)
+{
+	Proc *p;
+	Segment *s;
+	KMap *k;
+	Page *pg;
+
+	/* no processes yet */
+	up = nil;
+
+	p = newproc();
+	p->pgrp = newpgrp();
+	p->egrp = smalloc(sizeof(Egrp));
+	p->egrp->ref = 1;
+	p->fgrp = dupfgrp(nil);
+	p->rgrp = newrgrp();
+	p->procmode = 0640;
+
+	kstrdup(&eve, "");
+	kstrdup(&p->text, "*init*");
+	kstrdup(&p->user, eve);
+
+	/*
+	 * Kernel Stack
+	 */
+	p->sched.pc = PTR2UINT(init0);
+	p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->s.args)-sizeof(uintptr));
+	p->sched.sp = STACKALIGN(p->sched.sp);
+
+	/*
+	 * User Stack
+	 *
+	 * Technically, newpage can't be called here because it
+	 * should only be called when in a user context as it may
+	 * try to sleep if there are no pages available, but that
+	 * shouldn't be the case here.
+	 */
+	s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
+	s->flushme++;
+	p->seg[SSEG] = s;
+	pg = newpage(1, 0, USTKTOP-BY2PG);
+	segpage(s, pg);
+	k = kmap(pg);
+	bootargs(VA(k));
+	kunmap(k);
+
+	/*
+	 * Text
+	 */
+	s = newseg(SG_TEXT, UTZERO, 1);
+	p->seg[TSEG] = s;
+	pg = newpage(1, 0, UTZERO);
+	memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
+	segpage(s, pg);
+	k = kmap(s->map[0]->pages[0]);
+	memmove(UINT2PTR(VA(k)), initcode, sizeof initcode);
+	kunmap(k);
+
+	ready(p);
+}
+
+void
+confinit(void)
+{
+	int i;
+	ulong kpages;
+	uintptr pa;
+	char *p;
+
+	if(0 && (p = getconf("service")) != nil){
+		if(strcmp(p, "cpu") == 0)
+			cpuserver = 1;
+		else if(strcmp(p,"terminal") == 0)
+			cpuserver = 0;
+	}
+	if((p = getconf("*maxmem")) != nil){
+		memsize = strtoul(p, 0, 0) - PHYSDRAM;
+		if (memsize < 16*MB)		/* sanity */
+			memsize = 16*MB;
+	}
+
+	getramsize(&conf.mem[0]);
+	if(conf.mem[0].limit == 0){
+		conf.mem[0].base = PHYSDRAM;
+		conf.mem[0].limit = PHYSDRAM + memsize;
+	}else if(p != nil)
+		conf.mem[0].limit = conf.mem[0].base + memsize;
+
+	conf.npage = 0;
+	pa = PADDR(PGROUND(PTR2UINT(end)));
+
+	/*
+	 *  we assume that the kernel is at the beginning of one of the
+	 *  contiguous chunks of memory and fits therein.
+	 */
+	for(i=0; i<nelem(conf.mem); i++){
+		/* take kernel out of allocatable space */
+		if(pa > conf.mem[i].base && pa < conf.mem[i].limit)
+			conf.mem[i].base = pa;
+
+		conf.mem[i].npage = (conf.mem[i].limit - conf.mem[i].base)/BY2PG;
+		conf.npage += conf.mem[i].npage;
+	}
+
+	conf.upages = (conf.npage*80)/100;
+	conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG;
+
+	/* only one processor */
+	conf.nmach = 1;
+
+	/* set up other configuration parameters */
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	if(cpuserver)
+		conf.nproc *= 3;
+	if(conf.nproc > 2000)
+		conf.nproc = 2000;
+	conf.nswap = conf.npage*3;
+	conf.nswppo = 4096;
+	conf.nimage = 200;
+
+	conf.copymode = 0;		/* copy on write */
+
+	/*
+	 * Guess how much is taken by the large permanent
+	 * datastructures. Mntcache and Mntrpc are not accounted for
+	 * (probably ~300KB).
+	 */
+	kpages = conf.npage - conf.upages;
+	kpages *= BY2PG;
+	kpages -= conf.upages*sizeof(Page)
+		+ conf.nproc*sizeof(Proc)
+		+ conf.nimage*sizeof(Image)
+		+ conf.nswap
+		+ conf.nswppo*sizeof(Page);
+	mainmem->maxsize = kpages;
+	if(!cpuserver)
+		/*
+		 * give terminals lots of image memory, too; the dynamic
+		 * allocation will balance the load properly, hopefully.
+		 * be careful with 32-bit overflow.
+		 */
+		imagmem->maxsize = kpages;
+
+}
+
+static void
+shutdown(int ispanic)
+{
+	int ms, once;
+
+	lock(&active);
+	if(ispanic)
+		active.ispanic = ispanic;
+	else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
+		active.ispanic = 0;
+	once = active.machs & (1<<m->machno);
+	active.machs &= ~(1<<m->machno);
+	active.exiting = 1;
+	unlock(&active);
+
+	if(once)
+		iprint("cpu%d: exiting\n", m->machno);
+	spllo();
+	for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
+		delay(TK2MS(2));
+		if(active.machs == 0 && consactive() == 0)
+			break;
+	}
+	delay(1000);
+}
+
+/*
+ *  exit kernel either on a panic or user request
+ */
+void
+exit(int code)
+{
+	shutdown(code);
+	splfhi();
+	archreboot();
+}
+
+/*
+ * stub for ../omap/devether.c
+ */
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	USED(ctlrno);
+	USED(isa);
+	return strcmp(class, "ether") == 0;
+}
+
+/*
+ * the new kernel is already loaded at address `code'
+ * of size `size' and entry point `entry'.
+ */
+void
+reboot(void *entry, void *code, ulong size)
+{
+	void (*f)(ulong, ulong, ulong);
+
+	print("starting reboot...");
+	writeconf();
+	shutdown(0);
+
+	/*
+	 * should be the only processor running now
+	 */
+
+	print("reboot entry %#lux code %#lux size %ld\n",
+		PADDR(entry), PADDR(code), size);
+	delay(100);
+
+	/* turn off buffered serial console */
+	serialoq = nil;
+	kprintoq = nil;
+	screenputs = nil;
+
+	/* shutdown devices */
+	chandevshutdown();
+
+	/* stop the clock (and watchdog if any) */
+	clockshutdown();
+
+	splfhi();
+	intrsoff();
+
+	/* setup reboot trampoline function */
+	f = (void*)REBOOTADDR;
+	memmove(f, rebootcode, sizeof(rebootcode));
+	cacheuwbinv();
+
+	/* off we go - never to return */
+	(*f)(PADDR(entry), PADDR(code), size);
+
+	iprint("loaded kernel returned!\n");
+	delay(1000);
+	archreboot();
+}
+
+int
+cmpswap(long *addr, long old, long new)
+{
+	return cas32(addr, old, new);
+}
--- /dev/null
+++ b/sys/src/9/bcm/mem.h
@@ -1,0 +1,102 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+#define KiB		1024u			/* Kibi 0x0000000000000400 */
+#define MiB		1048576u		/* Mebi 0x0000000000100000 */
+#define GiB		1073741824u		/* Gibi 000000000040000000 */
+
+#define HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))	/* ceiling */
+#define ROUNDDN(x, y)	(((x)/(y))*(y))		/* floor */
+#define MIN(a, b)	((a) < (b)? (a): (b))
+#define MAX(a, b)	((a) > (b)? (a): (b))
+
+/*
+ * Sizes
+ */
+#define	BY2PG		(4*KiB)			/* bytes per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define	PGROUND(s)	ROUNDUP(s, BY2PG)
+#define	ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+#define	MACHSIZE	BY2PG
+
+#define KSTKSIZE	(8*KiB)
+#define STACKALIGN(sp)	((sp) & ~3)		/* bug: assure with alloc */
+
+/*
+ * Address spaces.
+ * KTZERO is used by kprof and dumpstack (if any).
+ *
+ * KZERO is mapped to physical 0 (start of ram).
+ *
+ * vectors are at 0, plan9.ini is at KZERO+256 and is limited to 16K by
+ * devenv.
+ */
+
+#define	KSEG0		0x80000000		/* kernel segment */
+/* mask to check segment; good for 512MB dram */
+#define	KSEGM		0xE0000000
+#define	KZERO		KSEG0			/* kernel address space */
+#define CONFADDR	(KZERO+0x100)		/* unparsed plan9.ini */
+#define	MACHADDR	(KZERO+0x2000)		/* Mach structure */
+#define	L2		(KZERO+0x3000)		/* L2 ptes for vectors etc */
+#define	VCBUFFER	(KZERO+0x3400)		/* videocore mailbox buffer */
+#define	FIQSTKTOP	(KZERO+0x4000)		/* FIQ stack */
+#define	L1		(KZERO+0x4000)		/* tt ptes: 16KiB aligned */
+#define	KTZERO		(KZERO+0x8000)		/* kernel text start */
+#define VIRTIO		0x7E000000		/* i/o registers */
+#define	FRAMEBUFFER	0xA0000000		/* video framebuffer */
+
+#define	UZERO		0			/* user segment */
+#define	UTZERO		(UZERO+BY2PG)		/* user text start */
+#define	USTKTOP		0x20000000		/* user segment end +1 */
+#define	USTKSIZE	(8*1024*1024)		/* user stack size */
+#define	TSTKTOP		(USTKTOP-USTKSIZE)	/* sysexec temporary stack */
+#define	TSTKSIZ	 	256
+
+/* address at which to copy and execute rebootcode */
+#define	REBOOTADDR	(KZERO+0x3400)
+
+/*
+ * Legacy...
+ */
+#define BLOCKALIGN	32			/* only used in allocb.c */
+#define KSTACK		KSTKSIZE
+
+/*
+ * Sizes
+ */
+#define BI2BY		8			/* bits per byte */
+#define BY2SE		4
+#define BY2WD		4
+#define BY2V		8			/* only used in xalloc.c */
+
+#define CACHELINESZ	32
+#define	PTEMAPMEM	(1024*1024)
+#define	PTEPERTAB	(PTEMAPMEM/BY2PG)
+#define	SEGMAPSIZE	1984
+#define	SSEGMAPSIZE	16
+#define	PPN(x)		((x)&~(BY2PG-1))
+
+/*
+ * With a little work these move to port.
+ */
+#define	PTEVALID	(1<<0)
+#define	PTERONLY	0
+#define	PTEWRITE	(1<<1)
+#define	PTEUNCACHED	(1<<2)
+#define PTEKERNEL	(1<<3)
+
+/*
+ * Physical machine information from here on.
+ *	PHYS addresses as seen from the arm cpu.
+ *	BUS  addresses as seen from the videocore gpu.
+ */
+#define	PHYSDRAM	0
+#define BUSDRAM		0x40000000
+#define	DRAMSIZE	(512*MiB)
+#define	PHYSIO		0x20000000
+#define	BUSIO		0x7E000000
+#define	IOSIZE		(16*MiB)
--- /dev/null
+++ b/sys/src/9/bcm/mkfile
@@ -1,0 +1,135 @@
+CONF=pif
+CONFLIST=pif picpuf
+EXTRACOPIES=piestand lookout boundary # bovril
+
+loadaddr=0x80008000
+
+objtype=arm
+</$objtype/mkfile
+p=9
+
+DEVS=`{rc ../port/mkdevlist $CONF}
+
+PORT=\
+	alarm.$O\
+	alloc.$O\
+	allocb.$O\
+	auth.$O\
+	cache.$O\
+	chan.$O\
+	dev.$O\
+	edf.$O\
+	fault.$O\
+	mul64fract.$O\
+	page.$O\
+	parse.$O\
+	pgrp.$O\
+	portclock.$O\
+	print.$O\
+	proc.$O\
+	qio.$O\
+	qlock.$O\
+	rdb.$O\
+	rebootcmd.$O\
+	segment.$O\
+	swap.$O\
+	syscallfmt.$O\
+	sysfile.$O\
+	sysproc.$O\
+	taslock.$O\
+	tod.$O\
+	xalloc.$O\
+
+OBJ=\
+	l.$O\
+	lexception.$O\
+	lproc.$O\
+	arch.$O\
+	clock.$O\
+	fpi.$O\
+	fpiarm.$O\
+	fpimem.$O\
+	main.$O\
+	mmu.$O\
+	random.$O\
+	syscall.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$CONF.rootc.$O\
+	$DEVS\
+	$PORT\
+
+# HFILES=
+
+LIB=\
+	/$objtype/lib/libmemlayer.a\
+	/$objtype/lib/libmemdraw.a\
+	/$objtype/lib/libdraw.a\
+	/$objtype/lib/libip.a\
+	/$objtype/lib/libsec.a\
+	/$objtype/lib/libmp.a\
+	/$objtype/lib/libc.a\
+
+9:V: $p$CONF s$p$CONF
+
+$p$CONF:DQ:	$CONF.c $OBJ $LIB mkfile
+	$CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c
+	echo '# linking raw kernel'	# H6: no headers, data segment aligned
+	$LD -l -o $target -H6 -R4096 -T$loadaddr $OBJ $CONF.$O $LIB
+
+s$p$CONF:DQ:	$CONF.$O $OBJ $LIB
+	echo '# linking kernel with symbols'
+	$LD -l -o $target -R4096 -T$loadaddr $OBJ $CONF.$O $LIB
+	size $target
+
+$p$CONF.gz:D:	$p$CONF
+	gzip -9 <$p$CONF >$target
+
+$OBJ: $HFILES
+
+install:V: /$objtype/$p$CONF
+
+/$objtype/$p$CONF:D: $p$CONF s$p$CONF
+	cp -x $p$CONF s$p$CONF /$objtype/ &
+	for(i in $EXTRACOPIES)
+		{ 9fs $i && cp $p$CONF s$p$CONF /n/$i/$objtype && echo -n $i... & }
+	wait
+	echo
+	touch $target
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+arch.$O clock.$O fpiarm.$O main.$O mmu.$O screen.$O syscall.$O trap.$O: \
+	/$objtype/include/ureg.h
+
+archbcm.$O: ../port/flashif.h
+fpi.$O fpiarm.$O fpimem.$O: fpi.h
+l.$O lexception.$O lproc.$O mmu.$O: arm.s mem.h
+main.$O: errstr.h init.h reboot.h
+devmouse.$O mouse.$O screen.$O: screen.h
+devusb.$O: ../port/usb.h
+
+init.h:D:	../port/initcode.c init9.s
+	$CC ../port/initcode.c
+	$AS init9.s
+	$LD -l -R1 -s -o init.out init9.$O initcode.$O /$objtype/lib/libc.a
+	{echo 'uchar initcode[]={'
+	 xd -1x <init.out |
+		sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
+	 echo '};'} > init.h
+
+reboot.h:D:	rebootcode.s arm.s arm.h mem.h
+	$AS rebootcode.s
+	# -lc is only for memmove.  -T arg is PADDR(REBOOTADDR)
+	$LD -l -s -T0x3400 -R4 -o reboot.out rebootcode.$O -lc
+	{echo 'uchar rebootcode[]={'
+	 xd -1x reboot.out |
+		sed -e '1,2d' -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
+	 echo '};'} > reboot.h
+errstr.h:D:	../port/mkerrstr ../port/error.h
+	rc ../port/mkerrstr > errstr.h
+
+$CONF.clean:
+	rm -rf $p$CONF s$p$CONF errstr.h reboot.h $CONF.c boot$CONF.c
--- /dev/null
+++ b/sys/src/9/bcm/mmu.c
@@ -1,0 +1,319 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "arm.h"
+
+#define FEXT(d, o, w)	(((d)>>(o)) & ((1<<(w))-1))
+#define L1X(va)		FEXT((va), 20, 12)
+#define L2X(va)		FEXT((va), 12, 8)
+
+enum {
+	L1lo		= UZERO/MiB,		/* L1X(UZERO)? */
+	L1hi		= (USTKTOP+MiB-1)/MiB,	/* L1X(USTKTOP+MiB-1)? */
+};
+
+void
+mmuinit(void)
+{
+	PTE *l1, *l2;
+	uintptr pa, va;
+
+	l1 = (PTE*)PADDR(L1);
+	l2 = (PTE*)PADDR(L2);
+
+	/*
+	 * map all of ram at KZERO
+	 */
+	va = KZERO;
+	for(pa = PHYSDRAM; pa < PHYSDRAM+DRAMSIZE; pa += MiB){
+		l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|Cached|Buffered;
+		va += MiB;
+	}
+
+	/*
+	 * identity map first MB of ram so mmu can be enabled
+	 */
+	l1[L1X(PHYSDRAM)] = PHYSDRAM|Dom0|L1AP(Krw)|Section|Cached|Buffered;
+
+	/*
+	 * map i/o registers 
+	 */
+	va = VIRTIO;
+	for(pa = PHYSIO; pa < PHYSIO+IOSIZE; pa += MiB){
+		l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section;
+		va += MiB;
+	}
+
+	/*
+	 * double map exception vectors at top of virtual memory
+	 */
+	va = HVECTORS;
+	l1[L1X(va)] = (uintptr)l2|Dom0|Coarse;
+	l2[L2X(va)] = PHYSDRAM|L2AP(Krw)|Small;
+}
+
+void
+mmuinit1(void)
+{
+	PTE *l1;
+
+	l1 = (PTE*)L1;
+	m->mmul1 = l1;
+
+	/*
+	 * undo identity map of first MB of ram
+	 */
+	l1[L1X(PHYSDRAM)] = 0;
+	cachedwbse(&l1[L1X(PHYSDRAM)], sizeof(PTE));
+	mmuinvalidate();
+}
+
+static void
+mmul2empty(Proc* proc, int clear)
+{
+	PTE *l1;
+	Page **l2, *page;
+
+	l1 = m->mmul1;
+	l2 = &proc->mmul2;
+	for(page = *l2; page != nil; page = page->next){
+		if(clear)
+			memset(UINT2PTR(page->va), 0, BY2PG);
+		l1[page->daddr] = Fault;
+		l2 = &page->next;
+	}
+	*l2 = proc->mmul2cache;
+	proc->mmul2cache = proc->mmul2;
+	proc->mmul2 = nil;
+}
+
+static void
+mmul1empty(void)
+{
+#ifdef notdef
+/* there's a bug in here */
+	PTE *l1;
+
+	/* clean out any user mappings still in l1 */
+	if(m->mmul1lo > L1lo){
+		if(m->mmul1lo == 1)
+			m->mmul1[L1lo] = Fault;
+		else
+			memset(&m->mmul1[L1lo], 0, m->mmul1lo*sizeof(PTE));
+		m->mmul1lo = L1lo;
+	}
+	if(m->mmul1hi < L1hi){
+		l1 = &m->mmul1[m->mmul1hi];
+		if((L1hi - m->mmul1hi) == 1)
+			*l1 = Fault;
+		else
+			memset(l1, 0, (L1hi - m->mmul1hi)*sizeof(PTE));
+		m->mmul1hi = L1hi;
+	}
+#else
+	memset(&m->mmul1[L1lo], 0, (L1hi - L1lo)*sizeof(PTE));
+#endif /* notdef */
+}
+
+void
+mmuswitch(Proc* proc)
+{
+	int x;
+	PTE *l1;
+	Page *page;
+
+	/* do kprocs get here and if so, do they need to? */
+	if(m->mmupid == proc->pid && !proc->newtlb)
+		return;
+	m->mmupid = proc->pid;
+
+	/* write back dirty and invalidate l1 caches */
+	cacheuwbinv();
+
+	if(proc->newtlb){
+		mmul2empty(proc, 1);
+		proc->newtlb = 0;
+	}
+
+	mmul1empty();
+
+	/* move in new map */
+	l1 = m->mmul1;
+	for(page = proc->mmul2; page != nil; page = page->next){
+		x = page->daddr;
+		l1[x] = PPN(page->pa)|Dom0|Coarse;
+		/* know here that L1lo < x < L1hi */
+		if(x+1 - m->mmul1lo < m->mmul1hi - x)
+			m->mmul1lo = x+1;
+		else
+			m->mmul1hi = x;
+	}
+
+	/* make sure map is in memory */
+	/* could be smarter about how much? */
+	cachedwbse(&l1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE));
+
+	/* lose any possible stale tlb entries */
+	mmuinvalidate();
+}
+
+void
+flushmmu(void)
+{
+	int s;
+
+	s = splhi();
+	up->newtlb = 1;
+	mmuswitch(up);
+	splx(s);
+}
+
+void
+mmurelease(Proc* proc)
+{
+	Page *page, *next;
+
+	/* write back dirty and invalidate l1 caches */
+	cacheuwbinv();
+
+	mmul2empty(proc, 0);
+	for(page = proc->mmul2cache; page != nil; page = next){
+		next = page->next;
+		if(--page->ref)
+			panic("mmurelease: page->ref %d", page->ref);
+		pagechainhead(page);
+	}
+	if(proc->mmul2cache && palloc.r.p)
+		wakeup(&palloc.r);
+	proc->mmul2cache = nil;
+
+	mmul1empty();
+
+	/* make sure map is in memory */
+	/* could be smarter about how much? */
+	cachedwbse(&m->mmul1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE));
+
+	/* lose any possible stale tlb entries */
+	mmuinvalidate();
+}
+
+void
+putmmu(uintptr va, uintptr pa, Page* page)
+{
+	int x;
+	Page *pg;
+	PTE *l1, *pte;
+
+	x = L1X(va);
+	l1 = &m->mmul1[x];
+	if(*l1 == Fault){
+		/* wasteful - l2 pages only have 256 entries - fix */
+		if(up->mmul2cache == nil){
+			/* auxpg since we don't need much? memset if so */
+			pg = newpage(1, 0, 0);
+			pg->va = VA(kmap(pg));
+		}
+		else{
+			pg = up->mmul2cache;
+			up->mmul2cache = pg->next;
+			memset(UINT2PTR(pg->va), 0, BY2PG);
+		}
+		pg->daddr = x;
+		pg->next = up->mmul2;
+		up->mmul2 = pg;
+
+		/* force l2 page to memory */
+		cachedwbse((void *)pg->va, BY2PG);
+
+		*l1 = PPN(pg->pa)|Dom0|Coarse;
+		cachedwbse(l1, sizeof *l1);
+
+		if(x >= m->mmul1lo && x < m->mmul1hi){
+			if(x+1 - m->mmul1lo < m->mmul1hi - x)
+				m->mmul1lo = x+1;
+			else
+				m->mmul1hi = x;
+		}
+	}
+	pte = UINT2PTR(KADDR(PPN(*l1)));
+
+	/* protection bits are
+	 *	PTERONLY|PTEVALID;
+	 *	PTEWRITE|PTEVALID;
+	 *	PTEWRITE|PTEUNCACHED|PTEVALID;
+	 */
+	x = Small;
+	if(!(pa & PTEUNCACHED))
+		x |= Cached|Buffered;
+	if(pa & PTEWRITE)
+		x |= L2AP(Urw);
+	else
+		x |= L2AP(Uro);
+	pte[L2X(va)] = PPN(pa)|x;
+	cachedwbse(&pte[L2X(va)], sizeof pte[0]);
+
+	/* clear out the current entry */
+	mmuinvalidateaddr(PPN(va));
+
+	/*  write back dirty entries - we need this because the pio() in
+	 *  fault.c is writing via a different virt addr and won't clean
+	 *  its changes out of the dcache.  Page coloring doesn't work
+	 *  on this mmu because the virtual cache is set associative
+	 *  rather than direct mapped.
+	 */
+	cachedwbinv();
+	if(page->cachectl[0] == PG_TXTFLUSH){
+		/* pio() sets PG_TXTFLUSH whenever a text pg has been written */
+		cacheiinv();
+		page->cachectl[0] = PG_NOFLUSH;
+	}
+	checkmmu(va, PPN(pa));
+}
+
+/*
+ * Return the number of bytes that can be accessed via KADDR(pa).
+ * If pa is not a valid argument to KADDR, return 0.
+ */
+uintptr
+cankaddr(uintptr pa)
+{
+	if(pa < PHYSDRAM + memsize)		/* assumes PHYSDRAM is 0 */
+		return PHYSDRAM + memsize - pa;
+	return 0;
+}
+
+uintptr
+mmukmap(uintptr va, uintptr pa, usize size)
+{
+	int o;
+	usize n;
+	PTE *pte, *pte0;
+
+	assert((va & (MiB-1)) == 0);
+	o = pa & (MiB-1);
+	pa -= o;
+	size += o;
+	pte = pte0 = &m->mmul1[L1X(va)];
+	for(n = 0; n < size; n += MiB)
+		if(*pte++ != Fault)
+			return 0;
+	pte = pte0;
+	for(n = 0; n < size; n += MiB){
+		*pte++ = (pa+n)|Dom0|L1AP(Krw)|Section;
+		mmuinvalidateaddr(va+n);
+	}
+	cachedwbse(pte0, pte - pte0);
+	return va + o;
+}
+
+
+void
+checkmmu(uintptr va, uintptr pa)
+{
+	USED(va);
+	USED(pa);
+}
+
--- /dev/null
+++ b/sys/src/9/bcm/mouse.c
@@ -1,0 +1,1 @@
+#include "../omap/mouse.c"
--- /dev/null
+++ b/sys/src/9/bcm/picpuf
@@ -1,0 +1,56 @@
+dev
+	root
+	cons
+	env
+	pipe
+	proc
+	mnt
+	srv
+	shr
+	dup
+	arch
+	ssl
+	tls
+	cap
+	fs
+	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno
+	draw	screen
+	mouse	mouse
+	uart
+
+	fakertc
+	sd
+	usb
+
+link
+	ethermedium
+	archbcm
+	usbdwc
+
+ip
+	tcp
+	udp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+misc
+	uartmini
+	sdmmc	emmc
+	dma
+	vcore
+	vfp3	coproc
+
+port
+	int cpuserver = 1;
+
+boot boot #S/sdM0/
+	local
+	tcp
+
+bootdir
+	boot$CONF.out	boot
+	/$objtype/bin/paqfs
+	/$objtype/bin/auth/factotum
+	bootfs.paq
--- /dev/null
+++ b/sys/src/9/bcm/pif
@@ -1,0 +1,56 @@
+dev
+	root
+	cons
+	env
+	pipe
+	proc
+	mnt
+	srv
+	shr
+	dup
+	arch
+	ssl
+	tls
+	cap
+	fs
+	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno
+	draw	screen
+	mouse	mouse
+	uart
+
+	fakertc
+	sd
+	usb
+
+link
+	ethermedium
+	archbcm
+	usbdwc
+
+ip
+	tcp
+	udp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+misc
+	uartmini
+	sdmmc	emmc
+	dma
+	vcore
+	vfp3	coproc
+
+port
+	int cpuserver = 0;
+
+boot boot #S/sdM0/
+	local
+	tcp
+
+bootdir
+	boot$CONF.out	boot
+	/$objtype/bin/paqfs
+	/$objtype/bin/auth/factotum
+	bootfs.paq
--- /dev/null
+++ b/sys/src/9/bcm/rebootcode.s
@@ -1,0 +1,93 @@
+/*
+ * armv6 reboot code
+ */
+#include "arm.s"
+
+/*
+ * Turn off MMU, then copy the new kernel to its correct location
+ * in physical memory.  Then jump to the start of the kernel.
+ */
+
+/* main(PADDR(entry), PADDR(code), size); */
+TEXT	main(SB), 1, $-4
+	MOVW	$setR12(SB), R12
+
+	/* copy in arguments before stack gets unmapped */
+	MOVW	R0, R8			/* entry point */
+	MOVW	p2+4(FP), R9		/* source */
+	MOVW	n+8(FP), R10		/* byte count */
+
+	/* SVC mode, interrupts disabled */
+	MOVW	$(PsrDirq|PsrDfiq|PsrMsvc), R1
+	MOVW	R1, CPSR
+
+	/* prepare to turn off mmu  */
+	BL	cachesoff(SB)
+
+	/* turn off mmu */
+	MRC	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+	BIC	$CpCmmu, R1
+	MCR	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+
+	/* set up a tiny stack for local vars and memmove args */
+	MOVW	R8, SP			/* stack top just before kernel dest */
+	SUB	$20, SP			/* allocate stack frame */
+
+	/* copy the kernel to final destination */
+	MOVW	R8, 16(SP)		/* save dest (entry point) */
+	MOVW	R8, R0			/* first arg is dest */
+	MOVW	R9, 8(SP)		/* push src */
+	MOVW	R10, 12(SP)		/* push size */
+	BL	memmove(SB)
+	MOVW	16(SP), R8		/* restore entry point */
+
+	/* jump to kernel physical entry point */
+	B	(R8)
+	B	0(PC)
+
+/*
+ * turn the caches off, double map PHYSDRAM & KZERO, invalidate TLBs, revert
+ * to tiny addresses.  upon return, it will be safe to turn off the mmu.
+ * clobbers R0-R2, and returns with SP invalid.
+ */
+TEXT cachesoff(SB), 1, $-4
+
+	/* write back and invalidate caches */
+	BARRIERS
+	MOVW	$0, R0
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEall
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
+
+	/* turn caches off */
+	MRC	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+	BIC	$(CpCdcache|CpCicache|CpCpredict), R1
+	MCR	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+
+	/* invalidate stale TLBs before changing them */
+	BARRIERS
+	MOVW	$KZERO, R0			/* some valid virtual address */
+	MCR	CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+	BARRIERS
+
+	/* from here on, R0 is base of physical memory */
+	MOVW	$PHYSDRAM, R0
+
+	/* redo double map of first MiB PHYSDRAM = KZERO */
+	MOVW	$(L1+L1X(PHYSDRAM)), R2		/* address of PHYSDRAM's PTE */
+	MOVW	$PTEDRAM, R1			/* PTE bits */
+	ORR	R0, R1				/* dram base */
+	MOVW	R1, (R2)
+
+	/* invalidate stale TLBs again */
+	BARRIERS
+	MCR	CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+	BARRIERS
+
+	/* relocate SB and return address to PHYSDRAM addressing */
+	MOVW	$KSEGM, R1		/* clear segment bits */
+	BIC	R1, R12			/* adjust SB */
+	ORR	R0, R12
+	BIC	R1, R14			/* adjust return address */
+	ORR	R0, R14
+
+	RET
--- /dev/null
+++ b/sys/src/9/bcm/screen.c
@@ -1,0 +1,544 @@
+/*
+ * bcm2385 framebuffer
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+enum {
+	Tabstop		= 4,
+	Scroll		= 8,
+	Wid		= 1024,
+	Ht		= 768,
+	Depth		= 16,
+};
+
+Cursor	arrow = {
+	{ -1, -1 },
+	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
+	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
+	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
+	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
+	},
+	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
+	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
+	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
+	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
+	},
+};
+
+Memimage *gscreen;
+
+static Memdata xgdata;
+
+static Memimage xgscreen =
+{
+	{ 0, 0, Wid, Ht },	/* r */
+	{ 0, 0, Wid, Ht },	/* clipr */
+	Depth,			/* depth */
+	3,			/* nchan */
+	RGB16,			/* chan */
+	nil,			/* cmap */
+	&xgdata,		/* data */
+	0,			/* zero */
+	0, 			/* width in words of a single scan line */
+	0,			/* layer */
+	0,			/* flags */
+};
+
+static Memimage *conscol;
+static Memimage *back;
+static Memsubfont *memdefont;
+
+static Lock screenlock;
+
+static Point	curpos;
+static int	h, w;
+static Rectangle window;
+
+static void myscreenputs(char *s, int n);
+static void screenputc(char *buf);
+static void screenwin(void);
+
+/*
+ * Software cursor. 
+ */
+static int	swvisible;	/* is the cursor visible? */
+static int	swenabled;	/* is the cursor supposed to be on the screen? */
+static Memimage *swback;	/* screen under cursor */
+static Memimage *swimg;		/* cursor image */
+static Memimage *swmask;	/* cursor mask */
+static Memimage *swimg1;
+static Memimage *swmask1;
+
+static Point	swoffset;
+static Rectangle swrect;	/* screen rectangle in swback */
+static Point	swpt;		/* desired cursor location */
+static Point	swvispt;	/* actual cursor location */
+static int	swvers;		/* incremented each time cursor image changes */
+static int	swvisvers;	/* the version on the screen */
+
+/*
+ * called with drawlock locked for us, most of the time.
+ * kernel prints at inopportune times might mean we don't
+ * hold the lock, but memimagedraw is now reentrant so
+ * that should be okay: worst case we get cursor droppings.
+ */
+static void
+swcursorhide(void)
+{
+	if(swvisible == 0)
+		return;
+	if(swback == nil)
+		return;
+	swvisible = 0;
+	memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
+	flushmemscreen(swrect);
+}
+
+static void
+swcursoravoid(Rectangle r)
+{
+	if(swvisible && rectXrect(r, swrect))
+		swcursorhide();
+}
+
+static void
+swcursordraw(void)
+{
+	int dounlock;
+
+	if(swvisible)
+		return;
+	if(swenabled == 0)
+		return;
+	if(swback == nil || swimg1 == nil || swmask1 == nil)
+		return;
+	dounlock = canqlock(&drawlock);
+	swvispt = swpt;
+	swvisvers = swvers;
+	swrect = rectaddpt(Rect(0,0,16,16), swvispt);
+	memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
+	memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
+	flushmemscreen(swrect);
+	swvisible = 1;
+	if(dounlock)
+		qunlock(&drawlock);
+}
+
+int
+cursoron(int dolock)
+{
+	int retry;
+
+	if (dolock)
+		lock(&cursor);
+	if (canqlock(&drawlock)) {
+		retry = 0;
+		swcursorhide();
+		swcursordraw();
+		qunlock(&drawlock);
+	} else
+		retry = 1;
+	if (dolock)
+		unlock(&cursor);
+	return retry;
+}
+
+void
+cursoroff(int dolock)
+{
+	if (dolock)
+		lock(&cursor);
+	swcursorhide();
+	if (dolock)
+		unlock(&cursor);
+}
+
+static void
+swload(Cursor *curs)
+{
+	uchar *ip, *mp;
+	int i, j, set, clr;
+
+	if(!swimg || !swmask || !swimg1 || !swmask1)
+		return;
+	/*
+	 * Build cursor image and mask.
+	 * Image is just the usual cursor image
+	 * but mask is a transparent alpha mask.
+	 * 
+	 * The 16x16x8 memimages do not have
+	 * padding at the end of their scan lines.
+	 */
+	ip = byteaddr(swimg, ZP);
+	mp = byteaddr(swmask, ZP);
+	for(i=0; i<32; i++){
+		set = curs->set[i];
+		clr = curs->clr[i];
+		for(j=0x80; j; j>>=1){
+			*ip++ = set&j ? 0x00 : 0xFF;
+			*mp++ = (clr|set)&j ? 0xFF : 0x00;
+		}
+	}
+	swoffset = curs->offset;
+	swvers++;
+	memimagedraw(swimg1,  swimg1->r,  swimg,  ZP, memopaque, ZP, S);
+	memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
+}
+
+/* called from devmouse */
+void
+setcursor(Cursor* curs)
+{
+	cursoroff(0);
+	swload(curs);
+	cursoron(0);
+}
+
+static int
+swmove(Point p)
+{
+	swpt = addpt(p, swoffset);
+	return 0;
+}
+
+static void
+swcursorclock(void)
+{
+	int x;
+
+	if(!swenabled)
+		return;
+	swmove(mousexy());
+	if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
+		return;
+
+	x = splhi();
+	if(swenabled)
+	if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
+	if(canqlock(&drawlock)){
+		swcursorhide();
+		swcursordraw();
+		qunlock(&drawlock);
+	}
+	splx(x);
+}
+
+void
+swcursorinit(void)
+{
+	static int init;
+
+	if(!init){
+		init = 1;
+		addclock0link(swcursorclock, 10);
+		swenabled = 1;
+	}
+	if(swback){
+		freememimage(swback);
+		freememimage(swmask);
+		freememimage(swmask1);
+		freememimage(swimg);
+		freememimage(swimg1);
+	}
+
+	swback  = allocmemimage(Rect(0,0,32,32), gscreen->chan);
+	swmask  = allocmemimage(Rect(0,0,16,16), GREY8);
+	swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
+	swimg   = allocmemimage(Rect(0,0,16,16), GREY8);
+	swimg1  = allocmemimage(Rect(0,0,16,16), GREY1);
+	if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
+		print("software cursor: allocmemimage fails\n");
+		return;
+	}
+
+	memfillcolor(swmask, DOpaque);
+	memfillcolor(swmask1, DOpaque);
+	memfillcolor(swimg, DBlack);
+	memfillcolor(swimg1, DBlack);
+}
+
+int
+hwdraw(Memdrawparam *par)
+{
+	Memimage *dst, *src, *mask;
+
+	if((dst=par->dst) == nil || dst->data == nil)
+		return 0;
+	if((src=par->src) == nil || src->data == nil)
+		return 0;
+	if((mask=par->mask) == nil || mask->data == nil)
+		return 0;
+
+	if(dst->data->bdata == xgdata.bdata)
+		swcursoravoid(par->r);
+	if(src->data->bdata == xgdata.bdata)
+		swcursoravoid(par->sr);
+	if(mask->data->bdata == xgdata.bdata)
+		swcursoravoid(par->mr);
+
+	return 0;
+}
+
+static int
+screensize(void)
+{
+	char *p;
+	char *f[3];
+	int width, height, depth;
+
+	p = getconf("vgasize");
+	if(p == nil || getfields(p, f, nelem(f), 0, "x") != nelem(f) ||
+	    (width = atoi(f[0])) < 16 || (height = atoi(f[1])) <= 0 ||
+	    (depth = atoi(f[2])) <= 0)
+		return -1;
+	xgscreen.r.max = Pt(width, height);
+	xgscreen.depth = depth;
+	return 0;
+}
+
+void
+screeninit(void)
+{
+	uchar *fb;
+	int set;
+	ulong chan;
+
+	set = screensize() == 0;
+	fb = fbinit(set, &xgscreen.r.max.x, &xgscreen.r.max.y, &xgscreen.depth);
+	if(fb == nil){
+		print("can't initialise %dx%dx%d framebuffer \n",
+			xgscreen.r.max.x, xgscreen.r.max.y, xgscreen.depth);
+		return;
+	}
+	xgscreen.clipr = xgscreen.r;
+	switch(xgscreen.depth){
+	default:
+		print("unsupported screen depth %d\n", xgscreen.depth);
+		xgscreen.depth = 16;
+		/* fall through */
+	case 16:
+		chan = RGB16;
+		break;
+	case 24:
+		chan = BGR24;
+		break;
+	case 32:
+		chan = ARGB32;
+		break;
+	}
+	memsetchan(&xgscreen, chan);
+	conf.monitor = 1;
+	xgdata.bdata = fb;
+	xgdata.ref = 1;
+	gscreen = &xgscreen;
+	gscreen->width = wordsperline(gscreen->r, gscreen->depth);
+
+	memimageinit();
+	memdefont = getmemdefont();
+	screenwin();
+	screenputs = myscreenputs;
+}
+
+void
+flushmemscreen(Rectangle)
+{
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen)
+{
+	*r = gscreen->r;
+	*d = gscreen->depth;
+	*chan = gscreen->chan;
+	*width = gscreen->width;
+	*softscreen = 0;
+
+	return gscreen->data->bdata;
+}
+
+void
+getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
+{
+	USED(p, pr, pg, pb);
+}
+
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+	USED(p, r, g, b);
+	return 0;
+}
+
+void
+blankscreen(int blank)
+{
+	fbblank(blank);
+}
+
+static void
+myscreenputs(char *s, int n)
+{
+	int i;
+	Rune r;
+	char buf[4];
+
+	if(!islo()) {
+		/* don't deadlock trying to print in interrupt */
+		if(!canlock(&screenlock))
+			return;	
+	}
+	else
+		lock(&screenlock);
+
+	while(n > 0){
+		i = chartorune(&r, s);
+		if(i == 0){
+			s++;
+			--n;
+			continue;
+		}
+		memmove(buf, s, i);
+		buf[i] = 0;
+		n -= i;
+		s += i;
+		screenputc(buf);
+	}
+	unlock(&screenlock);
+}
+
+static void
+screenwin(void)
+{
+	char *greet;
+	Memimage *orange;
+	Point p, q;
+	Rectangle r;
+
+	back = memwhite;
+	conscol = memblack;
+
+	orange = allocmemimage(Rect(0, 0, 1, 1), RGB16);
+	orange->flags |= Frepl;
+	orange->clipr = gscreen->r;
+	orange->data->bdata[0] = 0x40;		/* magic: colour? */
+	orange->data->bdata[1] = 0xfd;		/* magic: colour? */
+
+	w = memdefont->info[' '].width;
+	h = memdefont->height;
+
+	r = insetrect(gscreen->r, 4);
+
+	memimagedraw(gscreen, r, memblack, ZP, memopaque, ZP, S);
+	window = insetrect(r, 4);
+	memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP, S);
+
+	memimagedraw(gscreen, Rect(window.min.x, window.min.y,
+		window.max.x, window.min.y + h + 5 + 6), orange, ZP, nil, ZP, S);
+	freememimage(orange);
+	window = insetrect(window, 5);
+
+	greet = " Plan 9 Console ";
+	p = addpt(window.min, Pt(10, 0));
+	q = memsubfontwidth(memdefont, greet);
+	memimagestring(gscreen, p, conscol, ZP, memdefont, greet);
+	flushmemscreen(r);
+	window.min.y += h + 6;
+	curpos = window.min;
+	window.max.y = window.min.y + ((window.max.y - window.min.y) / h) * h;
+}
+
+static void
+scroll(void)
+{
+	int o;
+	Point p;
+	Rectangle r;
+
+	o = Scroll*h;
+	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
+	p = Pt(window.min.x, window.min.y+o);
+	memimagedraw(gscreen, r, gscreen, p, nil, p, S);
+	flushmemscreen(r);
+	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
+	memimagedraw(gscreen, r, back, ZP, nil, ZP, S);
+	flushmemscreen(r);
+
+	curpos.y -= o;
+}
+
+static void
+screenputc(char *buf)
+{
+	int w;
+	uint pos;
+	Point p;
+	Rectangle r;
+	static int *xp;
+	static int xbuf[256];
+
+	if (xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
+		xp = xbuf;
+
+	switch (buf[0]) {
+	case '\n':
+		if (curpos.y + h >= window.max.y)
+			scroll();
+		curpos.y += h;
+		screenputc("\r");
+		break;
+	case '\r':
+		xp = xbuf;
+		curpos.x = window.min.x;
+		break;
+	case '\t':
+		p = memsubfontwidth(memdefont, " ");
+		w = p.x;
+		if (curpos.x >= window.max.x - Tabstop * w)
+			screenputc("\n");
+
+		pos = (curpos.x - window.min.x) / w;
+		pos = Tabstop - pos % Tabstop;
+		*xp++ = curpos.x;
+		r = Rect(curpos.x, curpos.y, curpos.x + pos * w, curpos.y + h);
+		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
+		flushmemscreen(r);
+		curpos.x += pos * w;
+		break;
+	case '\b':
+		if (xp <= xbuf)
+			break;
+		xp--;
+		r = Rect(*xp, curpos.y, curpos.x, curpos.y + h);
+		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
+		flushmemscreen(r);
+		curpos.x = *xp;
+		break;
+	case '\0':
+		break;
+	default:
+		p = memsubfontwidth(memdefont, buf);
+		w = p.x;
+
+		if (curpos.x >= window.max.x - w)
+			screenputc("\n");
+
+		*xp++ = curpos.x;
+		r = Rect(curpos.x, curpos.y, curpos.x + w, curpos.y + h);
+		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
+		memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
+		flushmemscreen(r);
+		curpos.x += w;
+		break;
+	}
+}
--- /dev/null
+++ b/sys/src/9/bcm/screen.h
@@ -1,0 +1,36 @@
+typedef struct Cursor Cursor;
+typedef struct Cursorinfo Cursorinfo;
+struct Cursorinfo {
+	Cursor;
+	Lock;
+};
+
+/* devmouse.c */
+extern void mousetrack(int, int, int, int);
+extern void absmousetrack(int, int, int, int);
+extern Point mousexy(void);
+
+extern void mouseaccelerate(int);
+extern int m3mouseputc(Queue*, int);
+extern int m5mouseputc(Queue*, int);
+extern int mouseputc(Queue*, int);
+
+extern Cursorinfo cursor;
+extern Cursor arrow;
+
+/* mouse.c */
+extern void mousectl(Cmdbuf*);
+extern void mouseresize(void);
+
+/* screen.c */
+extern void	blankscreen(int);
+extern void	flushmemscreen(Rectangle);
+extern uchar*	attachscreen(Rectangle*, ulong*, int*, int*, int*);
+extern int	cursoron(int);
+extern void	cursoroff(int);
+extern void	setcursor(Cursor*);
+
+/* devdraw.c */
+extern QLock	drawlock;
+
+#define ishwimage(i)	1		/* for ../port/devdraw.c */
--- /dev/null
+++ b/sys/src/9/bcm/softfpu.c
@@ -1,0 +1,1 @@
+#include "../teg2/softfpu.c"
--- /dev/null
+++ b/sys/src/9/bcm/syscall.c
@@ -1,0 +1,1 @@
+#include "../kw/syscall.c"
--- /dev/null
+++ b/sys/src/9/bcm/trap.c
@@ -1,0 +1,567 @@
+/*
+ * traps, exceptions, interrupts, system calls.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "arm.h"
+
+#define INTREGS		(VIRTIO+0xB200)
+
+typedef struct Intregs Intregs;
+typedef struct Vctl Vctl;
+
+enum {
+	Nvec = 8,		/* # of vectors at start of lexception.s */
+	Fiqenable = 1<<7,
+};
+
+/*
+ *   Layout at virtual address KZERO (double mapped at HVECTORS).
+ */
+typedef struct Vpage0 {
+	void	(*vectors[Nvec])(void);
+	u32int	vtable[Nvec];
+} Vpage0;
+
+/*
+ * interrupt control registers
+ */
+struct Intregs {
+	u32int	ARMpending;
+	u32int	GPUpending[2];
+	u32int	FIQctl;
+	u32int	GPUenable[2];
+	u32int	ARMenable;
+	u32int	GPUdisable[2];
+	u32int	ARMdisable;
+};
+
+struct Vctl {
+	Vctl	*next;
+	int	irq;
+	u32int	*reg;
+	u32int	mask;
+	void	(*f)(Ureg*, void*);
+	void	*a;
+};
+
+static Vctl *vctl, *vfiq;
+
+static char *trapnames[PsrMask+1] = {
+	[ PsrMusr ] "user mode",
+	[ PsrMfiq ] "fiq interrupt",
+	[ PsrMirq ] "irq interrupt",
+	[ PsrMsvc ] "svc/swi exception",
+	[ PsrMabt ] "prefetch abort/data abort",
+	[ PsrMabt+1 ] "data abort",
+	[ PsrMund ] "undefined instruction",
+	[ PsrMsys ] "sys trap",
+};
+
+extern int notify(Ureg*);
+
+/*
+ *  set up for exceptions
+ */
+void
+trapinit(void)
+{
+	Vpage0 *vpage0;
+
+	/* disable everything */
+	intrsoff();
+
+	/* set up the exception vectors */
+	vpage0 = (Vpage0*)HVECTORS;
+	memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors));
+	memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable));
+	cacheuwbinv();
+
+	/* set up the stacks for the interrupt modes */
+	setr13(PsrMfiq, (u32int*)(FIQSTKTOP));
+	setr13(PsrMirq, m->sirq);
+	setr13(PsrMabt, m->sabt);
+	setr13(PsrMund, m->sund);
+	setr13(PsrMsys, m->ssys);
+
+	coherence();
+}
+
+void
+intrsoff(void)
+{
+	Intregs *ip;
+	int disable;
+
+	ip = (Intregs*)INTREGS;
+	disable = ~0;
+	ip->GPUdisable[0] = disable;
+	ip->GPUdisable[1] = disable;
+	ip->ARMdisable = disable;
+	ip->FIQctl = 0;
+}
+
+/*
+ *  called by trap to handle irq interrupts.
+ *  returns true iff a clock interrupt, thus maybe reschedule.
+ */
+static int
+irq(Ureg* ureg)
+{
+	Vctl *v;
+	int clockintr;
+
+	clockintr = 0;
+	for(v = vctl; v; v = v->next)
+		if(*v->reg & v->mask){
+			coherence();
+			v->f(ureg, v->a);
+			coherence();
+			if(v->irq == IRQclock)
+				clockintr = 1;
+		}
+	return clockintr;
+}
+
+/*
+ * called direct from lexception.s to handle fiq interrupt.
+ */
+void
+fiq(Ureg *ureg)
+{
+	Vctl *v;
+
+	v = vfiq;
+	if(v == nil)
+		panic("unexpected item in bagging area");
+	m->intr++;
+	ureg->pc -= 4;
+	coherence();
+	v->f(ureg, v->a);
+	coherence();
+}
+
+void
+irqenable(int irq, void (*f)(Ureg*, void*), void* a)
+{
+	Vctl *v;
+	Intregs *ip;
+	u32int *enable;
+
+	ip = (Intregs*)INTREGS;
+	v = (Vctl*)malloc(sizeof(Vctl));
+	if(v == nil)
+		panic("irqenable: no mem");
+	v->irq = irq;
+	if(irq >= IRQbasic){
+		enable = &ip->ARMenable;
+		v->reg = &ip->ARMpending;
+		v->mask = 1 << (irq - IRQbasic);
+	}else{
+		enable = &ip->GPUenable[irq/32];
+		v->reg = &ip->GPUpending[irq/32];
+		v->mask = 1 << (irq % 32);
+	}
+	v->f = f;
+	v->a = a;
+	if(irq == IRQfiq){
+		assert((ip->FIQctl & Fiqenable) == 0);
+		assert((*enable & v->mask) == 0);
+		vfiq = v;
+		ip->FIQctl = Fiqenable | irq;
+	}else{
+		v->next = vctl;
+		vctl = v;
+		*enable = v->mask;
+	}
+}
+
+static char *
+trapname(int psr)
+{
+	char *s;
+
+	s = trapnames[psr & PsrMask];
+	if(s == nil)
+		s = "unknown trap number in psr";
+	return s;
+}
+
+/*
+ *  called by trap to handle access faults
+ */
+static void
+faultarm(Ureg *ureg, uintptr va, int user, int read)
+{
+	int n, insyscall;
+	char buf[ERRMAX];
+	static int cnt, lastpid;
+	static ulong lastva;
+
+	if(up == nil) {
+		dumpregs(ureg);
+		panic("fault: nil up in faultarm, accessing %#p", va);
+	}
+	insyscall = up->insyscall;
+	up->insyscall = 1;
+	/* this is quite helpful during mmu and cache debugging */
+	if(va == lastva && up->pid == lastpid) {
+		++cnt;
+		if (cnt >= 2)
+			/* fault() isn't fixing the underlying cause */
+			panic("fault: %d consecutive faults for va %#lux",
+				cnt+1, va);
+	} else {
+		cnt = 0;
+		lastva = va;
+		lastpid = up->pid;
+	}
+
+	n = fault(va, read);
+	if(n < 0){
+		if(!user){
+			dumpregs(ureg);
+			panic("fault: kernel accessing %#p", va);
+		}
+		/* don't dump registers; programs suicide all the time */
+		snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p",
+			read? "read": "write", va);
+		postnote(up, 1, buf, NDebug);
+	}
+	up->insyscall = insyscall;
+}
+
+/*
+ *  returns 1 if the instruction writes memory, 0 otherwise
+ */
+int
+writetomem(ulong inst)
+{
+	/* swap always write memory */
+	if((inst & 0x0FC00000) == 0x01000000)
+		return 1;
+
+	/* loads and stores are distinguished by bit 20 */
+	if(inst & (1<<20))
+		return 0;
+
+	return 1;
+}
+
+/*
+ *  here on all exceptions other than syscall (SWI) and fiq
+ */
+void
+trap(Ureg *ureg)
+{
+	int clockintr, user, x, rv, rem;
+	ulong inst, fsr;
+	uintptr va;
+	char buf[ERRMAX];
+
+	assert(!islo());
+	if(up != nil)
+		rem = ((char*)ureg)-up->kstack;
+	else
+		rem = ((char*)ureg)-((char*)m+sizeof(Mach));
+	if(rem < 256) {
+		iprint("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux\n",
+			rem, up, ureg, ureg->pc);
+		delay(1000);
+		dumpstack();
+		panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux",
+			rem, up, ureg, ureg->pc);
+	}
+
+	user = (ureg->psr & PsrMask) == PsrMusr;
+	if(user){
+		up->dbgreg = ureg;
+		cycles(&up->kentry);
+	}
+
+	/*
+	 * All interrupts/exceptions should be resumed at ureg->pc-4,
+	 * except for Data Abort which resumes at ureg->pc-8.
+	 */
+	if(ureg->type == (PsrMabt+1))
+		ureg->pc -= 8;
+	else
+		ureg->pc -= 4;
+
+	clockintr = 0;		/* if set, may call sched() before return */
+	switch(ureg->type){
+	default:
+		panic("unknown trap; type %#lux, psr mode %#lux", ureg->type,
+			ureg->psr & PsrMask);
+		break;
+	case PsrMirq:
+		clockintr = irq(ureg);
+		m->intr++;
+		break;
+	case PsrMabt:			/* prefetch fault */
+		x = ifsrget();
+		fsr = (x>>7) & 0x8 | x & 0x7;
+		switch(fsr){
+		case 0x02:		/* instruction debug event (BKPT) */
+			if(user){
+				snprint(buf, sizeof buf, "sys: breakpoint");
+				postnote(up, 1, buf, NDebug);
+			}else{
+				iprint("kernel bkpt: pc %#lux inst %#ux\n",
+					ureg->pc, *(u32int*)ureg->pc);
+				panic("kernel bkpt");
+			}
+			break;
+		default:
+			faultarm(ureg, ureg->pc, user, 1);
+			break;
+		}
+		break;
+	case PsrMabt+1:			/* data fault */
+		va = farget();
+		inst = *(ulong*)(ureg->pc);
+		/* bits 12 and 10 have to be concatenated with status */
+		x = fsrget();
+		fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf;
+		switch(fsr){
+		default:
+		case 0xa:		/* ? was under external abort */
+			panic("unknown data fault, 6b fsr %#lux", fsr);
+			break;
+		case 0x0:
+			panic("vector exception at %#lux", ureg->pc);
+			break;
+		case 0x1:		/* alignment fault */
+		case 0x3:		/* access flag fault (section) */
+			if(user){
+				snprint(buf, sizeof buf,
+					"sys: alignment: pc %#lux va %#p\n",
+					ureg->pc, va);
+				postnote(up, 1, buf, NDebug);
+			} else
+				panic("kernel alignment: pc %#lux va %#p", ureg->pc, va);
+			break;
+		case 0x2:
+			panic("terminal exception at %#lux", ureg->pc);
+			break;
+		case 0x4:		/* icache maint fault */
+		case 0x6:		/* access flag fault (page) */
+		case 0x8:		/* precise external abort, non-xlat'n */
+		case 0x28:
+		case 0xc:		/* l1 translation, precise ext. abort */
+		case 0x2c:
+		case 0xe:		/* l2 translation, precise ext. abort */
+		case 0x2e:
+		case 0x16:		/* imprecise ext. abort, non-xlt'n */
+		case 0x36:
+			panic("external abort %#lux pc %#lux addr %#p",
+				fsr, ureg->pc, va);
+			break;
+		case 0x1c:		/* l1 translation, precise parity err */
+		case 0x1e:		/* l2 translation, precise parity err */
+		case 0x18:		/* imprecise parity or ecc err */
+			panic("translation parity error %#lux pc %#lux addr %#p",
+				fsr, ureg->pc, va);
+			break;
+		case 0x5:		/* translation fault, no section entry */
+		case 0x7:		/* translation fault, no page entry */
+			faultarm(ureg, va, user, !writetomem(inst));
+			break;
+		case 0x9:
+		case 0xb:
+			/* domain fault, accessing something we shouldn't */
+			if(user){
+				snprint(buf, sizeof buf,
+					"sys: access violation: pc %#lux va %#p\n",
+					ureg->pc, va);
+				postnote(up, 1, buf, NDebug);
+			} else
+				panic("kernel access violation: pc %#lux va %#p",
+					ureg->pc, va);
+			break;
+		case 0xd:
+		case 0xf:
+			/* permission error, copy on write or real permission error */
+			faultarm(ureg, va, user, !writetomem(inst));
+			break;
+		}
+		break;
+	case PsrMund:			/* undefined instruction */
+		if(user){
+			if(seg(up, ureg->pc, 0) != nil &&
+			   *(u32int*)ureg->pc == 0xD1200070)
+				postnote(up, 1, "sys: breakpoint", NDebug);
+			else{
+				/* look for floating point instructions to interpret */
+				rv = fpuemu(ureg);
+				if(rv == 0){
+					snprint(buf, sizeof buf,
+						"undefined instruction: pc %#lux\n",
+						ureg->pc);
+					postnote(up, 1, buf, NDebug);
+				}
+			}
+		}else{
+			if (ureg->pc & 3) {
+				iprint("rounding fault pc %#lux down to word\n",
+					ureg->pc);
+				ureg->pc &= ~3;
+			}
+			iprint("undefined instruction: pc %#lux inst %#ux\n",
+				ureg->pc, *(u32int*)ureg->pc);
+			panic("undefined instruction");
+		}
+		break;
+	}
+	splhi();
+
+	/* delaysched set because we held a lock or because our quantum ended */
+	if(up && up->delaysched && clockintr){
+		sched();		/* can cause more traps */
+		splhi();
+	}
+
+	if(user){
+		if(up->procctl || up->nnote)
+			notify(ureg);
+		kexit(ureg);
+	}
+}
+
+int
+isvalidaddr(void *v)
+{
+	return (uintptr)v >= KZERO;
+}
+
+static void
+dumplongs(char *msg, ulong *v, int n)
+{
+	int i, l;
+
+	l = 0;
+	iprint("%s at %.8p: ", msg, v);
+	for(i=0; i<n; i++){
+		if(l >= 4){
+			iprint("\n    %.8p: ", v);
+			l = 0;
+		}
+		if(isvalidaddr(v)){
+			iprint(" %.8lux", *v++);
+			l++;
+		}else{
+			iprint(" invalid");
+			break;
+		}
+	}
+	iprint("\n");
+}
+
+static void
+dumpstackwithureg(Ureg *ureg)
+{
+	uintptr l, i, v, estack;
+	u32int *p;
+	char *s;
+
+	if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
+		iprint("dumpstack disabled\n");
+		return;
+	}
+	iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
+		ureg->pc, ureg->sp, ureg->r14);
+	delay(2000);
+	i = 0;
+	if(up != nil && (uintptr)&l <= (uintptr)up->kstack+KSTACK)
+		estack = (uintptr)up->kstack+KSTACK;
+	else if((uintptr)&l >= (uintptr)m->stack
+	     && (uintptr)&l <= (uintptr)m+MACHSIZE)
+		estack = (uintptr)m+MACHSIZE;
+	else{
+		if(up != nil)
+			iprint("&up->kstack %#p &l %#p\n", up->kstack, &l);
+		else
+			iprint("&m %#p &l %#p\n", m, &l);
+		return;
+	}
+	for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
+		v = *(uintptr*)l;
+		if(KTZERO < v && v < (uintptr)etext && !(v & 3)){
+			v -= sizeof(u32int);		/* back up an instr */
+			p = (u32int*)v;
+			if((*p & 0x0f000000) == 0x0b000000){	/* BL instr? */
+				iprint("%#8.8lux=%#8.8lux ", l, v);
+				i++;
+			}
+		}
+		if(i == 4){
+			i = 0;
+			iprint("\n");
+		}
+	}
+	if(i)
+		iprint("\n");
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+void
+callwithureg(void (*fn)(Ureg*))
+{
+	Ureg ureg;
+
+	ureg.pc = getcallerpc(&fn);
+	ureg.sp = PTR2UINT(&fn);
+	fn(&ureg);
+}
+
+void
+dumpstack(void)
+{
+	callwithureg(dumpstackwithureg);
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+	int s;
+
+	if (ureg == nil) {
+		iprint("trap: no user process\n");
+		return;
+	}
+	s = splhi();
+	iprint("trap: %s", trapname(ureg->type));
+	if(ureg != nil && (ureg->psr & PsrMask) != PsrMsvc)
+		iprint(" in %s", trapname(ureg->psr));
+	iprint("\n");
+	iprint("psr %8.8lux type %2.2lux pc %8.8lux link %8.8lux\n",
+		ureg->psr, ureg->type, ureg->pc, ureg->link);
+	iprint("R14 %8.8lux R13 %8.8lux R12 %8.8lux R11 %8.8lux R10 %8.8lux\n",
+		ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+	iprint("R9  %8.8lux R8  %8.8lux R7  %8.8lux R6  %8.8lux R5  %8.8lux\n",
+		ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+	iprint("R4  %8.8lux R3  %8.8lux R2  %8.8lux R1  %8.8lux R0  %8.8lux\n",
+		ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+	iprint("stack is at %#p\n", ureg);
+	iprint("pc %#lux link %#lux\n", ureg->pc, ureg->link);
+
+	if(up)
+		iprint("user stack: %#p-%#p\n", up->kstack, up->kstack+KSTACK-4);
+	else
+		iprint("kernel stack: %8.8lux-%8.8lux\n",
+			(ulong)(m+1), (ulong)m+BY2PG-4);
+	dumplongs("stack", (ulong *)(ureg + 1), 16);
+	delay(2000);
+	dumpstack();
+	splx(s);
+}
--- /dev/null
+++ b/sys/src/9/bcm/uartmini.c
@@ -1,0 +1,413 @@
+/*
+ * bcm2835 mini uart (UART1)
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define GPIOREGS	(VIRTIO+0x200000)
+#define AUXREGS		(VIRTIO+0x215000)
+#define	OkLed		16
+#define	TxPin		14
+#define	RxPin		15
+
+/* GPIO regs */
+enum {
+	Fsel0	= 0x00>>2,
+		FuncMask= 0x7,
+		Input	= 0x0,
+		Output	= 0x1,
+		Alt0	= 0x4,
+		Alt1	= 0x5,
+		Alt2	= 0x6,
+		Alt3	= 0x7,
+		Alt4	= 0x3,
+		Alt5	= 0x2,
+	Set0	= 0x1c>>2,
+	Clr0	= 0x28>>2,
+	Lev0	= 0x34>>2,
+	PUD	= 0x94>>2,
+		Off	= 0x0,
+		Pulldown= 0x1,
+		Pullup	= 0x2,
+	PUDclk0	= 0x98>>2,
+	PUDclk1	= 0x9c>>2,
+};
+
+/* AUX regs */
+enum {
+	Irq	= 0x00>>2,
+		UartIrq	= 1<<0,
+	Enables	= 0x04>>2,
+		UartEn	= 1<<0,
+	MuIo	= 0x40>>2,
+	MuIer	= 0x44>>2,
+		RxIen	= 1<<0,
+		TxIen	= 1<<1,
+	MuIir	= 0x48>>2,
+	MuLcr	= 0x4c>>2,
+		Bitsmask= 3<<0,
+		Bits7	= 2<<0,
+		Bits8	= 3<<0,
+	MuMcr	= 0x50>>2,
+		RtsN	= 1<<1,
+	MuLsr	= 0x54>>2,
+		TxDone	= 1<<6,
+		TxRdy	= 1<<5,
+		RxRdy	= 1<<0,
+	MuCntl	= 0x60>>2,
+		CtsFlow	= 1<<3,
+		TxEn	= 1<<1,
+		RxEn	= 1<<0,
+	MuBaud	= 0x68>>2,
+};
+
+extern PhysUart miniphysuart;
+
+static Uart miniuart = {
+	.regs	= (u32int*)AUXREGS,
+	.name	= "uart0",
+	.freq	= 250000000,
+	.phys	= &miniphysuart,
+};
+
+void
+gpiosel(uint pin, int func)
+{	
+	u32int *gp, *fsel;
+	int off;
+
+	gp = (u32int*)GPIOREGS;
+	fsel = &gp[Fsel0 + pin/10];
+	off = (pin % 10) * 3;
+	*fsel = (*fsel & ~(FuncMask << off)) | func << off;
+}
+
+void
+gpiopulloff(uint pin)
+{
+	u32int *gp, *reg;
+	u32int mask;
+
+	gp = (u32int*)GPIOREGS;
+	reg = &gp[PUDclk0 + pin/32];
+	mask = 1 << (pin % 32);
+	gp[PUD] = Off;
+	microdelay(1);
+	*reg = mask;
+	microdelay(1);
+	*reg = 0;
+}
+
+void
+gpioout(uint pin, int set)
+{
+	u32int *gp;
+	int v;
+
+	gp = (u32int*)GPIOREGS;
+	v = set? Set0: Clr0;
+	gp[v + pin/32] = 1 << (pin % 32);
+}
+
+int
+gpioin(uint pin)
+{
+	u32int *gp;
+
+	gp = (u32int*)GPIOREGS;
+	return (gp[Lev0 + pin/32] & (1 << (pin % 32))) != 0;
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	Uart *uart;
+	u32int *ap;
+
+	uart = arg;
+	ap = (u32int*)uart->regs;
+
+	coherence();
+	if(0 && (ap[Irq] & UartIrq) == 0)
+		return;
+	if(ap[MuLsr] & TxRdy)
+		uartkick(uart);
+	if(ap[MuLsr] & RxRdy){
+		do{
+			uartrecv(uart, ap[MuIo] & 0xFF);
+		}while(ap[MuLsr] & RxRdy);
+	}
+	coherence();
+}
+
+static Uart*
+pnp(void)
+{
+	return &miniuart;
+}
+
+static void
+enable(Uart *uart, int ie)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	delay(10);
+	gpiosel(TxPin, Alt5);
+	gpiosel(RxPin, Alt5);
+	gpiopulloff(TxPin);
+	gpiopulloff(RxPin);
+	ap[Enables] |= UartEn;
+	ap[MuIir] = 6;
+	ap[MuLcr] = Bits8;
+	ap[MuCntl] = TxEn|RxEn;
+	ap[MuBaud] = 250000000 / (115200 * 8) - 1;
+	if(ie){
+		intrenable(IRQaux, interrupt, uart, 0, "uart");
+		ap[MuIer] = RxIen|TxIen;
+	}else
+		ap[MuIer] = 0;
+}
+
+static void
+disable(Uart *uart)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	ap[MuCntl] = 0;
+	ap[MuIer] = 0;
+}
+
+static void
+kick(Uart *uart)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	if(uart->blocked)
+		return;
+	coherence();
+	while(ap[MuLsr] & TxRdy){
+		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+			break;
+		ap[MuIo] = *(uart->op++);
+	}
+	if(ap[MuLsr] & TxDone)
+		ap[MuIer] &= ~TxIen;
+	else
+		ap[MuIer] |= TxIen;
+	coherence();
+}
+
+/* TODO */
+static void
+dobreak(Uart *uart, int ms)
+{
+	USED(uart, ms);
+}
+
+static int
+baud(Uart *uart, int n)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	if(uart->freq == 0 || n <= 0)
+		return -1;
+	ap[MuBaud] = (uart->freq + 4*n - 1) / (8 * n) - 1;
+	uart->baud = n;
+	return 0;
+}
+
+static int
+bits(Uart *uart, int n)
+{
+	u32int *ap;
+	int set;
+
+	ap = (u32int*)uart->regs;
+	switch(n){
+	case 7:
+		set = Bits7;
+		break;
+	case 8:
+		set = Bits8;
+		break;
+	default:
+		return -1;
+	}
+	ap[MuLcr] = (ap[MuLcr] & ~Bitsmask) | set;
+	uart->bits = n;
+	return 0;
+}
+
+static int
+stop(Uart *uart, int n)
+{
+	if(n != 1)
+		return -1;
+	uart->stop = n;
+	return 0;
+}
+
+static int
+parity(Uart *uart, int n)
+{
+	if(n != 'n')
+		return -1;
+	uart->parity = n;
+	return 0;
+}
+
+/*
+ * cts/rts flow control
+ *   need to bring signals to gpio pins before enabling this
+ */
+
+static void
+modemctl(Uart *uart, int on)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	if(on)
+		ap[MuCntl] |= CtsFlow;
+	else
+		ap[MuCntl] &= ~CtsFlow;
+	uart->modem = on;
+}
+
+static void
+rts(Uart *uart, int on)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	if(on)
+		ap[MuMcr] &= ~RtsN;
+	else
+		ap[MuMcr] |= RtsN;
+}
+
+static long
+status(Uart *uart, void *buf, long n, long offset)
+{
+	char *p;
+
+	p = malloc(READSTR);
+	if(p == nil)
+		error(Enomem);
+	snprint(p, READSTR,
+		"b%d\n"
+		"dev(%d) type(%d) framing(%d) overruns(%d) "
+		"berr(%d) serr(%d)\n",
+
+		uart->baud,
+		uart->dev,
+		uart->type,
+		uart->ferr,
+		uart->oerr,
+		uart->berr,
+		uart->serr
+	);
+	n = readstr(offset, buf, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+donothing(Uart*, int)
+{
+}
+
+void
+putc(Uart*, int c)
+{
+	u32int *ap;
+
+	ap = (u32int*)AUXREGS;
+	while((ap[MuLsr] & TxRdy) == 0)
+		;
+	ap[MuIo] = c;
+	while((ap[MuLsr] & TxRdy) == 0)
+		;
+}
+
+int
+getc(Uart*)
+{
+	u32int *ap;
+
+	ap = (u32int*)AUXREGS;
+	while((ap[MuLsr] & RxRdy) == 0)
+		;
+	return ap[MuIo] & 0xFF;
+}
+
+void
+uartconsinit(void)
+{
+	Uart *uart;
+	int n;
+	char *p, *cmd;
+
+	if((p = getconf("console")) == nil)
+		return;
+	n = strtoul(p, &cmd, 0);
+	if(p == cmd)
+		return;
+	switch(n){
+	default:
+		return;
+	case 0:
+		uart = &miniuart;
+		break;
+	}
+
+	if(!uart->enabled)
+		(*uart->phys->enable)(uart, 0);
+	uartctl(uart, "b9600 l8 pn s1");
+	if(*cmd != '\0')
+		uartctl(uart, cmd);
+
+	consuart = uart;
+	uart->console = 1;
+}
+
+PhysUart miniphysuart = {
+	.name		= "miniuart",
+	.pnp		= pnp,
+	.enable		= enable,
+	.disable	= disable,
+	.kick		= kick,
+	.dobreak	= dobreak,
+	.baud		= baud,
+	.bits		= bits,
+	.stop		= stop,
+	.parity		= parity,
+	.modemctl	= donothing,
+	.rts		= rts,
+	.dtr		= donothing,
+	.status		= status,
+	.fifo		= donothing,
+	.getc		= getc,
+	.putc		= putc,
+};
+
+void
+okay(int on)
+{
+	static int first;
+
+	if(!first++)
+		gpiosel(OkLed, Output);
+	gpioout(OkLed, !on);
+}
--- /dev/null
+++ b/sys/src/9/bcm/usbdwc.c
@@ -1,0 +1,965 @@
+/*
+ * USB host driver for BCM2835
+ *	Synopsis DesignWare Core USB 2.0 OTG controller
+ *
+ * Copyright © 2012 Richard Miller <[email protected]>
+ *
+ * This is work in progress:
+ * - no isochronous pipes
+ * - no bandwidth budgeting
+ * - frame scheduling is crude
+ * - error handling is overly optimistic
+ * It should be just about adequate for a Plan 9 terminal with
+ * keyboard, mouse, ethernet adapter, and an external flash drive.
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+#include	"../port/usb.h"
+
+#include "dwcotg.h"
+
+enum
+{
+	USBREGS		= VIRTIO + 0x980000,
+	Enabledelay	= 50,
+	Resetdelay	= 10,
+	ResetdelayHS	= 50,
+
+	Read		= 0,
+	Write		= 1,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Epio Epio;
+
+struct Ctlr {
+	Dwcregs	*regs;		/* controller registers */
+	int	nchan;		/* number of host channels */
+	ulong	chanbusy;	/* bitmap of in-use channels */
+	QLock	chanlock;	/* serialise access to chanbusy */
+	QLock	split;		/* serialise split transactions */
+	int	splitretry;	/* count retries of Nyet */
+	int	sofchan;	/* bitmap of channels waiting for sof */
+	int	wakechan;	/* bitmap of channels to wakeup after fiq */
+	int	debugchan;	/* bitmap of channels for interrupt debug */
+	Rendez	*chanintr;	/* sleep till interrupt on channel N */
+};
+
+struct Epio {
+	QLock;
+	Block	*cb;
+	ulong	lastpoll;
+};
+
+static Ctlr dwc;
+static int debug;
+
+static char Ebadlen[] = "bad usb request length";
+static char Enotconfig[] = "usb endpoint not configured";
+
+static void clog(Ep *ep, Hostchan *hc);
+static void logdump(Ep *ep);
+
+static Hostchan*
+chanalloc(Ep *ep)
+{
+	Ctlr *ctlr;
+	int bitmap, i;
+
+	ctlr = ep->hp->aux;
+	qlock(&ctlr->chanlock);
+	bitmap = ctlr->chanbusy;
+	for(i = 0; i < ctlr->nchan; i++)
+		if((bitmap & (1<<i)) == 0){
+			ctlr->chanbusy = bitmap | 1 << i;
+			qunlock(&ctlr->chanlock);
+			return &ctlr->regs->hchan[i];
+		}
+	qunlock(&ctlr->chanlock);
+	panic("miller is a lazy git");
+	return nil;
+}
+
+static void
+chanrelease(Ep *ep, Hostchan *chan)
+{
+	Ctlr *ctlr;
+	int i;
+
+	ctlr = ep->hp->aux;
+	i = chan - ctlr->regs->hchan;
+	qlock(&ctlr->chanlock);
+	ctlr->chanbusy &= ~(1 << i);
+	qunlock(&ctlr->chanlock);
+}
+
+static void
+chansetup(Hostchan *hc, Ep *ep)
+{
+	int hcc;
+	Ctlr *ctlr = ep->hp->aux;
+
+	if(ep->debug)
+		ctlr->debugchan |= 1 << (hc - ctlr->regs->hchan);
+	else
+		ctlr->debugchan &= ~(1 << (hc - ctlr->regs->hchan));
+	switch(ep->dev->state){
+	case Dconfig:
+	case Dreset:
+		hcc = 0;
+		break;
+	default:
+		hcc = ep->dev->nb<<ODevaddr;
+		break;
+	}
+	hcc |= ep->maxpkt | 1<<OMulticnt | ep->nb<<OEpnum;
+	switch(ep->ttype){
+	case Tctl:
+		hcc |= Epctl;
+		break;
+	case Tiso:
+		hcc |= Episo;
+		break;
+	case Tbulk:
+		hcc |= Epbulk;
+		break;
+	case Tintr:
+		hcc |= Epintr;
+		break;
+	}
+	switch(ep->dev->speed){
+	case Lowspeed:
+		hcc |= Lspddev;
+		/* fall through */
+	case Fullspeed:
+		hc->hcsplt = Spltena | POS_ALL | ep->dev->hub << OHubaddr |
+			ep->dev->port;
+		break;
+	default:
+		hc->hcsplt = 0;
+		break;
+	}
+	hc->hcchar = hcc;
+	hc->hcint = ~0;
+}
+
+static int
+sofdone(void *a)
+{
+	Dwcregs *r;
+
+	r = a;
+	return r->gintsts & Sofintr;
+}
+
+static void
+sofwait(Ctlr *ctlr, int n)
+{
+	Dwcregs *r;
+	int x;
+
+	r = ctlr->regs;
+	do{
+		r->gintsts = Sofintr;
+		x = splfhi();
+		ctlr->sofchan |= 1 << n;
+		r->gintmsk |= Sofintr;
+		sleep(&ctlr->chanintr[n], sofdone, r);
+		splx(x);
+	}while((r->hfnum & 7) == 6);
+}
+
+static int
+chandone(void *a)
+{
+	Hostchan *hc;
+
+	hc = a;
+	return (hc->hcint & hc->hcintmsk) != 0;
+}
+
+static int
+chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
+{
+	int intr, n, x, ointr;
+	ulong start, now;
+	Dwcregs *r;
+
+	r = ctlr->regs;
+	n = hc - r->hchan;
+	for(;;){
+restart:
+		x = splfhi();
+		r->haintmsk |= 1 << n;
+		hc->hcintmsk = mask;
+		sleep(&ctlr->chanintr[n], chandone, hc);
+		hc->hcintmsk = 0;
+		splx(x);
+		intr = hc->hcint;
+		if(intr & Chhltd)
+			return intr;
+		start = fastticks(0);
+		ointr = intr;
+		now = start;
+		do{
+			intr = hc->hcint;
+			if(intr & Chhltd){
+				if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
+				    intr != (Ack|Chhltd|Xfercomp) ||
+				    (now - start) > 60)
+					dprint("await %x after %ld %x -> %x\n",
+						mask, now - start, ointr, intr);
+				return intr;
+			}
+			if((intr & mask) == 0){
+				dprint("ep%d.%d await %x intr %x -> %x\n",						ep->dev->nb, ep->nb, mask, ointr, intr);
+				goto restart;
+			}
+			now = fastticks(0);
+		}while(now - start < 100);
+		dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
+			"grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
+			ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
+			r->gnptxsts, r->hptxsts);
+		mask = Chhltd;
+		hc->hcchar |= Chdis;
+		start = m->ticks;
+		while(hc->hcchar & Chen){
+			if(m->ticks - start >= 100){
+				print("ep%d.%d channel won't halt hcchar %8.8ux\n",
+					ep->dev->nb, ep->nb, hc->hcchar);
+				break;
+			}
+		}
+		logdump(ep);
+	}
+}
+
+static int
+chanintr(Ctlr *ctlr, int n)
+{
+	Hostchan *hc;
+	int i;
+
+	hc = &ctlr->regs->hchan[n];
+	if(ctlr->debugchan & (1 << n))
+		clog(nil, hc);
+	if((hc->hcsplt & Spltena) == 0)
+		return 0;
+	i = hc->hcint;
+	if(i == (Chhltd|Ack)){
+		hc->hcsplt |= Compsplt;
+		ctlr->splitretry = 0;
+	}else if(i == (Chhltd|Nyet)){
+		if(++ctlr->splitretry >= 3)
+			return 0;
+	}else
+		return 0;
+	if(hc->hcchar & Chen){
+		iprint("hcchar %8.8ux hcint %8.8ux", hc->hcchar, hc->hcint);
+		hc->hcchar |= Chen | Chdis;
+		while(hc->hcchar&Chen)
+			;
+		iprint(" %8.8ux\n", hc->hcint);
+	}
+	hc->hcint = i;
+	if(ctlr->regs->hfnum & 1)
+		hc->hcchar &= ~Oddfrm;
+	else
+		hc->hcchar |= Oddfrm;
+	hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
+	return 1;
+}
+
+static Reg chanlog[32][5];
+static int nchanlog;
+
+static void
+logstart(Ep *ep)
+{
+	if(ep->debug)
+		nchanlog = 0;
+}
+
+static void
+clog(Ep *ep, Hostchan *hc)
+{
+	Reg *p;
+
+	if(ep != nil && !ep->debug)
+		return;
+	if(nchanlog == 32)
+		nchanlog--;
+	p = chanlog[nchanlog];
+	p[0] = dwc.regs->hfnum;
+	p[1] = hc->hcchar;
+	p[2] = hc->hcint;
+	p[3] = hc->hctsiz;
+	p[4] = hc->hcdma;
+	nchanlog++;
+}
+
+static void
+logdump(Ep *ep)
+{
+	Reg *p;
+	int i;
+
+	if(!ep->debug)
+		return;
+	p = chanlog[0];
+	for(i = 0; i < nchanlog; i++){
+		print("%5.5d.%5.5d %8.8ux %8.8ux %8.8ux %8.8ux\n",
+			p[0]&0xFFFF, p[0]>>16, p[1], p[2], p[3], p[4]);
+		p += 5;
+	}
+	nchanlog = 0;
+}
+
+static int
+chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
+{
+	Ctlr *ctlr;
+	int nleft, n, nt, i, maxpkt, npkt;
+	uint hcdma, hctsiz;
+
+	ctlr = ep->hp->aux;
+	maxpkt = ep->maxpkt;
+	npkt = HOWMANY(len, ep->maxpkt);
+	if(npkt == 0)
+		npkt = 1;
+
+	hc->hcchar = (hc->hcchar & ~Epdir) | dir;
+	if(dir == Epin)
+		n = ROUND(len, ep->maxpkt);
+	else
+		n = len;
+	hc->hctsiz = n | npkt << OPktcnt | pid;
+	hc->hcdma  = PADDR(a);
+
+	nleft = len;
+	logstart(ep);
+	for(;;){
+		hcdma = hc->hcdma;
+		hctsiz = hc->hctsiz;
+		hc->hctsiz = hctsiz & ~Dopng;
+		if(hc->hcchar&Chen){
+			dprint("ep%d.%d before chanio hcchar=%8.8ux\n",
+				ep->dev->nb, ep->nb, hc->hcchar);
+			hc->hcchar |= Chen | Chdis;
+			while(hc->hcchar&Chen)
+				;
+			hc->hcint = Chhltd;
+		}
+		if((i = hc->hcint) != 0){
+			dprint("ep%d.%d before chanio hcint=%8.8ux\n",
+				ep->dev->nb, ep->nb, i);
+			hc->hcint = i;
+		}
+		if(hc->hcsplt & Spltena){
+			qlock(&ctlr->split);
+			sofwait(ctlr, hc - ctlr->regs->hchan);
+			if((dwc.regs->hfnum & 1) == 0)
+				hc->hcchar &= ~Oddfrm;
+			else
+				hc->hcchar |= Oddfrm;
+		}
+		hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
+		clog(ep, hc);
+		if(ep->ttype == Tbulk && dir == Epin)
+			i = chanwait(ep, ctlr, hc, /* Ack| */ Chhltd);
+		else if(ep->ttype == Tintr && (hc->hcsplt & Spltena))
+			i = chanwait(ep, ctlr, hc, Chhltd);
+		else
+			i = chanwait(ep, ctlr, hc, Chhltd|Nak);
+		clog(ep, hc);
+		hc->hcint = i;
+
+		if(hc->hcsplt & Spltena){
+			hc->hcsplt &= ~Compsplt;
+			qunlock(&ctlr->split);
+		}
+
+		if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
+			if(i & Stall)
+				error(Estalled);
+			if(i & Nyet)
+				continue;
+			if(i & Nak){
+				if(ep->ttype == Tintr)
+					tsleep(&up->sleep, return0, 0, ep->pollival);
+				else
+					tsleep(&up->sleep, return0, 0, 1);
+				continue;
+			}
+			print("usbotg: ep%d.%d error intr %8.8ux\n",
+				ep->dev->nb, ep->nb, i);
+			if(i & ~(Chhltd|Ack))
+				error(Eio);
+			if(hc->hcdma != hcdma)
+				print("usbotg: weird hcdma %x->%x intr %x->%x\n",
+					hcdma, hc->hcdma, i, hc->hcint);
+		}
+		n = hc->hcdma - hcdma;
+		if(n == 0)
+			if((hc->hctsiz & Pktcnt) != (hctsiz & Pktcnt))
+				break;
+			else
+				continue;
+		if(dir == Epin && ep->ttype == Tbulk && n == nleft){
+			nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize);
+			if(nt != n)
+				if(n == ((nt+3) & ~3))
+					n = nt;
+				else
+					print("usbotg: intr %8.8ux dma "
+						"%8.8ux-%8.8ux hctsiz "
+						"%8.8ux-%8.ux\n",
+						i, hcdma, hc->hcdma, hctsiz,
+						hc->hctsiz);
+		}
+		if(n > nleft){
+			if(n != ((nleft+3) & ~3))
+				dprint("too much: wanted %d got %d\n",
+					len, len - nleft + n);
+			n = nleft;
+		}
+		nleft -= n;
+		if(nleft == 0 || n % maxpkt != 0)
+			break;
+		if((i & Xfercomp) && ep->ttype != Tctl)
+			break;
+		if(dir == Epout)
+			dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n",
+				nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i);
+	}
+	logdump(ep);
+	return len - nleft;
+}
+
+static long
+eptrans(Ep *ep, int rw, void *a, long n)
+{
+	Hostchan *hc;
+
+	if(ep->clrhalt){
+		ep->clrhalt = 0;
+		if(ep->mode != OREAD)
+			ep->toggle[Write] = DATA0;
+		if(ep->mode != OWRITE)
+			ep->toggle[Read] = DATA0;
+	}
+	hc = chanalloc(ep);
+	if(waserror()){
+		ep->toggle[rw] = hc->hctsiz & Pid;
+		chanrelease(ep, hc);
+		if(strcmp(up->errstr, Estalled) == 0)
+			return 0;
+		nexterror();
+	}
+	chansetup(hc, ep);
+	if(rw == Read && ep->ttype == Tbulk){
+		long sofar, m;
+
+		sofar = 0;
+		do{
+			m = n - sofar;
+			if(m > ep->maxpkt)
+				m = ep->maxpkt;
+			m = chanio(ep, hc, Epin, ep->toggle[rw],
+				(char*)a + sofar, m);
+			ep->toggle[rw] = hc->hctsiz & Pid;
+			sofar += m;
+		}while(sofar < n && m == ep->maxpkt);
+		n = sofar;
+	}else{
+		n = chanio(ep, hc, rw == Read? Epin: Epout, ep->toggle[rw],
+			a, n);
+		ep->toggle[rw] = hc->hctsiz & Pid;
+	}
+	chanrelease(ep, hc);
+	poperror();
+	return n;
+}
+
+static long
+ctltrans(Ep *ep, uchar *req, long n)
+{
+	Hostchan *hc;
+	Epio *epio;
+	Block *b;
+	uchar *data;
+	int datalen;
+
+	epio = ep->aux;
+	if(epio->cb != nil){
+		freeb(epio->cb);
+		epio->cb = nil;
+	}
+	if(n < Rsetuplen)
+		error(Ebadlen);
+	if(req[Rtype] & Rd2h){
+		datalen = GET2(req+Rcount);
+		if(datalen <= 0 || datalen > Maxctllen)
+			error(Ebadlen);
+		/* XXX cache madness */
+		epio->cb = b = allocb(ROUND(datalen, ep->maxpkt) + CACHELINESZ);
+		b->wp = (uchar*)ROUND((uintptr)b->wp, CACHELINESZ);
+		memset(b->wp, 0x55, b->lim - b->wp);
+		cachedwbinvse(b->wp, b->lim - b->wp);
+		data = b->wp;
+	}else{
+		b = nil;
+		datalen = n - Rsetuplen;
+		data = req + Rsetuplen;
+	}
+	hc = chanalloc(ep);
+	if(waserror()){
+		chanrelease(ep, hc);
+		if(strcmp(up->errstr, Estalled) == 0)
+			return 0;
+		nexterror();
+	}
+	chansetup(hc, ep);
+	chanio(ep, hc, Epout, SETUP, req, Rsetuplen);
+	if(req[Rtype] & Rd2h){
+		b->wp += chanio(ep, hc, Epin, DATA1, data, datalen);
+		chanio(ep, hc, Epout, DATA1, nil, 0);
+		n = Rsetuplen;
+	}else{
+		if(datalen > 0)
+			chanio(ep, hc, Epout, DATA1, data, datalen);
+		chanio(ep, hc, Epin, DATA1, nil, 0);
+		n = Rsetuplen + datalen;
+	}
+	chanrelease(ep, hc);
+	poperror();
+	return n;
+}
+
+static long
+ctldata(Ep *ep, void *a, long n)
+{
+	Epio *epio;
+	Block *b;
+
+	epio = ep->aux;
+	b = epio->cb;
+	if(b == nil)
+		return 0;
+	if(n > BLEN(b))
+		n = BLEN(b);
+	memmove(a, b->rp, n);
+	b->rp += n;
+	if(BLEN(b) == 0){
+		freeb(b);
+		epio->cb = nil;
+	}
+	return n;
+}
+
+static void
+greset(Dwcregs *r, int bits)
+{
+	r->grstctl |= bits;
+	while(r->grstctl & bits)
+		;
+	microdelay(10);
+}
+
+static void
+init(Hci *hp)
+{
+	Ctlr *ctlr;
+	Dwcregs *r;
+	uint n, rx, tx, ptx;
+
+	ctlr = hp->aux;
+	r = ctlr->regs;
+
+	ctlr->nchan = 1 + ((r->ghwcfg2 & Num_host_chan) >> ONum_host_chan);
+	ctlr->chanintr = malloc(ctlr->nchan * sizeof(Rendez));
+
+	r->gahbcfg = 0;
+	setpower(PowerUsb, 1);
+
+	while((r->grstctl&Ahbidle) == 0)
+		;
+	greset(r, Csftrst);
+
+	r->gusbcfg |= Force_host_mode;
+	tsleep(&up->sleep, return0, 0, 25);
+	r->gahbcfg |= Dmaenable;
+
+	n = (r->ghwcfg3 & Dfifo_depth) >> ODfifo_depth;
+	rx = 0x306;
+	tx = 0x100;
+	ptx = 0x200;
+	r->grxfsiz = rx;
+	r->gnptxfsiz = rx | tx << ODepth;
+	tsleep(&up->sleep, return0, 0, 1);
+	r->hptxfsiz = (rx + tx) | ptx << ODepth;
+	greset(r, Rxfflsh);
+	r->grstctl = TXF_ALL;
+	greset(r, Txfflsh);
+	dprint("usbotg: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n",
+		n, r->grxfsiz, r->gnptxfsiz, r->hptxfsiz);
+
+	r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng;
+	r->gintsts = ~0;
+	r->gintmsk = Hcintr;
+	r->gahbcfg |= Glblintrmsk;
+}
+
+static void
+dump(Hci*)
+{
+}
+
+static void
+fiqintr(Ureg*, void *a)
+{
+	Hci *hp;
+	Ctlr *ctlr;
+	Dwcregs *r;
+	uint intr, haint, wakechan;
+	int i;
+
+	hp = a;
+	ctlr = hp->aux;
+	r = ctlr->regs;
+	wakechan = 0;
+	intr = r->gintsts;
+	if(intr & Hcintr){
+		haint = r->haint & r->haintmsk;
+		for(i = 0; haint; i++){
+			if(haint & 1 && chanintr(ctlr, i) == 0){
+				r->haintmsk &= ~(1 << i);
+				wakechan |= 1 << i;
+			}
+			haint >>= 1;
+		}
+	}
+	if(intr & Sofintr){
+		r->gintsts = Sofintr;
+		if((r->hfnum&7) != 6){
+			r->gintmsk &= ~Sofintr;
+			wakechan |= ctlr->sofchan;
+			ctlr->sofchan = 0;
+		}
+	}
+	if(wakechan){
+		ctlr->wakechan |= wakechan;
+		armtimerset(1);
+	}
+}
+
+static void
+irqintr(Ureg*, void *a)
+{
+	Ctlr *ctlr;
+	uint wakechan;
+	int i, x;
+
+	ctlr = a;
+	x = splfhi();
+	armtimerset(0);
+	wakechan = ctlr->wakechan;
+	ctlr->wakechan = 0;
+	splx(x);
+	for(i = 0; wakechan; i++){
+		if(wakechan & 1)
+			wakeup(&ctlr->chanintr[i]);
+		wakechan >>= 1;
+	}
+}
+
+static void
+epopen(Ep *ep)
+{
+	ddprint("usbotg: epopen ep%d.%d ttype %d\n",
+		ep->dev->nb, ep->nb, ep->ttype);
+	switch(ep->ttype){
+	case Tnone:
+		error(Enotconfig);
+	case Tintr:
+		assert(ep->pollival > 0);
+		/* fall through */
+	case Tbulk:
+		if(ep->toggle[Read] == 0)
+			ep->toggle[Read] = DATA0;
+		if(ep->toggle[Write] == 0)
+			ep->toggle[Write] = DATA0;
+		break;
+	}
+	ep->aux = malloc(sizeof(Epio));
+	if(ep->aux == nil)
+		error(Enomem);
+}
+
+static void
+epclose(Ep *ep)
+{
+	ddprint("usbotg: epclose ep%d.%d ttype %d\n",
+		ep->dev->nb, ep->nb, ep->ttype);
+	switch(ep->ttype){
+	case Tctl:
+		freeb(((Epio*)ep->aux)->cb);
+		/* fall through */
+	default:
+		free(ep->aux);
+		break;
+	}
+}
+
+static long
+epread(Ep *ep, void *a, long n)
+{
+	Epio *epio;
+	Block *b;
+	uchar *p;
+	ulong elapsed;
+	long nr;
+
+	ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
+	epio = ep->aux;
+	b = nil;
+	qlock(epio);
+	if(waserror()){
+		qunlock(epio);
+		if(b)
+			freeb(b);
+		nexterror();
+	}
+	switch(ep->ttype){
+	default:
+		error(Egreg);
+	case Tctl:
+		nr = ctldata(ep, a, n);
+		qunlock(epio);
+		poperror();
+		return nr;
+	case Tintr:
+		elapsed = TK2MS(m->ticks) - epio->lastpoll;
+		if(elapsed < ep->pollival)
+			tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
+		/* fall through */
+	case Tbulk:
+		/* XXX cache madness */
+		b = allocb(ROUND(n, ep->maxpkt) + CACHELINESZ);
+		p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
+		cachedwbinvse(p, n);
+		nr = eptrans(ep, Read, p, n);
+		epio->lastpoll = TK2MS(m->ticks);
+		memmove(a, p, nr);
+		qunlock(epio);
+		freeb(b);
+		poperror();
+		return nr;
+	}
+}
+
+static long
+epwrite(Ep *ep, void *a, long n)
+{
+	Epio *epio;
+	Block *b;
+	uchar *p;
+	ulong elapsed;
+
+	ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
+	epio = ep->aux;
+	b = nil;
+	qlock(epio);
+	if(waserror()){
+		qunlock(epio);
+		if(b)
+			freeb(b);
+		nexterror();
+	}
+	switch(ep->ttype){
+	default:
+		error(Egreg);
+	case Tintr:
+		elapsed = TK2MS(m->ticks) - epio->lastpoll;
+		if(elapsed < ep->pollival)
+			tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
+		/* fall through */
+	case Tctl:
+	case Tbulk:
+		/* XXX cache madness */
+		b = allocb(n + CACHELINESZ);
+		p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
+		memmove(p, a, n);
+		cachedwbse(p, n);
+		if(ep->ttype == Tctl)
+			n = ctltrans(ep, p, n);
+		else{
+			n = eptrans(ep, Write, p, n);
+			epio->lastpoll = TK2MS(m->ticks);
+		}
+		qunlock(epio);
+		freeb(b);
+		poperror();
+		return n;
+	}
+}
+
+static char*
+seprintep(char *s, char*, Ep*)
+{
+	return s;
+}
+	
+static int
+portenable(Hci *hp, int port, int on)
+{
+	Ctlr *ctlr;
+	Dwcregs *r;
+
+	assert(port == 1);
+	ctlr = hp->aux;
+	r = ctlr->regs;
+	dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
+	if(!on)
+		r->hport0 = Prtpwr | Prtena;
+	tsleep(&up->sleep, return0, 0, Enabledelay);
+	dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
+	return 0;
+}
+
+static int
+portreset(Hci *hp, int port, int on)
+{
+	Ctlr *ctlr;
+	Dwcregs *r;
+	int b, s;
+
+	assert(port == 1);
+	ctlr = hp->aux;
+	r = ctlr->regs;
+	dprint("usbotg reset=%d; sts %#x\n", on, r->hport0);
+	if(!on)
+		return 0;
+	r->hport0 = Prtpwr | Prtrst;
+	tsleep(&up->sleep, return0, 0, ResetdelayHS);
+	r->hport0 = Prtpwr;
+	tsleep(&up->sleep, return0, 0, Enabledelay);
+	s = r->hport0;
+	b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
+	if(b != 0)
+		r->hport0 = Prtpwr | b;
+	dprint("usbotg reset=%d; sts %#x\n", on, s);
+	if((s & Prtena) == 0)
+		print("usbotg: host port not enabled after reset");
+	return 0;
+}
+
+static int
+portstatus(Hci *hp, int port)
+{
+	Ctlr *ctlr;
+	Dwcregs *r;
+	int b, s;
+
+	assert(port == 1);
+	ctlr = hp->aux;
+	r = ctlr->regs;
+	s = r->hport0;
+	b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
+	if(b != 0)
+		r->hport0 = Prtpwr | b;
+	b = 0;
+	if(s & Prtconnsts)
+		b |= HPpresent;
+	if(s & Prtconndet)
+		b |= HPstatuschg;
+	if(s & Prtena)
+		b |= HPenable;
+	if(s & Prtenchng)
+		b |= HPchange;
+	if(s & Prtovrcurract)
+		 b |= HPovercurrent;
+	if(s & Prtsusp)
+		b |= HPsuspend;
+	if(s & Prtrst)
+		b |= HPreset;
+	if(s & Prtpwr)
+		b |= HPpower;
+	switch(s & Prtspd){
+	case HIGHSPEED:
+		b |= HPhigh;
+		break;
+	case LOWSPEED:
+		b |= HPslow;
+		break;
+	}
+	return b;
+}
+
+static void
+shutdown(Hci*)
+{
+}
+
+static void
+setdebug(Hci*, int d)
+{
+	debug = d;
+}
+
+static int
+reset(Hci *hp)
+{
+	Ctlr *ctlr;
+	uint id;
+
+	ctlr = &dwc;
+	if(ctlr->regs != nil)
+		return -1;
+	ctlr->regs = (Dwcregs*)USBREGS;
+	id = ctlr->regs->gsnpsid;
+	if((id>>16) != ('O'<<8 | 'T'))
+		return -1;
+	dprint("usbotg: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
+
+	intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc");
+
+	hp->aux = ctlr;
+	hp->port = 0;
+	hp->irq = IRQusb;
+	hp->tbdf = 0;
+	hp->nports = 1;
+	hp->highspeed = 1;
+
+	hp->init = init;
+	hp->dump = dump;
+	hp->interrupt = fiqintr;
+	hp->epopen = epopen;
+	hp->epclose = epclose;
+	hp->epread = epread;
+	hp->epwrite = epwrite;
+	hp->seprintep = seprintep;
+	hp->portenable = portenable;
+	hp->portreset = portreset;
+	hp->portstatus = portstatus;
+	hp->shutdown = shutdown;
+	hp->debug = setdebug;
+	hp->type = "dwcotg";
+
+	intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, "usbdwcotg");
+
+	return 0;
+}
+
+void
+usbdwclink(void)
+{
+	addhcitype("dwcotg", reset);
+}
--- /dev/null
+++ b/sys/src/9/bcm/vcore.c
@@ -1,0 +1,290 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * Mailbox interface with videocore gpu
+ */
+
+#define	MAILBOX		(VIRTIO+0xB880)
+
+typedef struct Prophdr Prophdr;
+typedef struct Fbinfo Fbinfo;
+
+enum {
+	Read		= 0x00>>2,
+	Write		= 0x00>>2,
+	Peek		= 0x10>>2,
+	Sender		= 0x14>>2,
+	Status		= 0x18>>2,
+		Full		= 1<<31,
+		Empty		= 1<<30,
+	Config		= 0x1C>>2,
+	NRegs		= 0x20>>2,
+
+	ChanMask	= 0xF,
+	ChanProps	= 8,
+	ChanFb		= 1,
+
+	Req			= 0x0,
+	RspOk		= 0x80000000,
+	TagResp		= 1<<31,
+
+	TagGetfwrev	= 0x00000001,
+	TagGetmac	= 0x00010003,
+	TagGetram	= 0x00010005,
+	TagGetpower	= 0x00020001,
+	TagSetpower	= 0x00028001,
+		Powerwait	= 1<<1,
+	TagGetclkspd= 0x00030002,
+	TagFballoc	= 0x00040001,
+	TagFbfree	= 0x00048001,
+	TagFbblank	= 0x00040002,
+	TagGetres	= 0x00040003,
+	TagSetres	= 0x00048003,
+	TagGetvres	= 0x00040004,
+	TagSetvres	= 0x00048004,
+	TagGetdepth	= 0x00040005,
+	TagSetdepth	= 0x00048005,
+	TagGetrgb	= 0x00044006,
+	TagSetrgb	= 0x00048006,
+};
+
+struct Fbinfo {
+	u32int	xres;
+	u32int	yres;
+	u32int	xresvirtual;
+	u32int	yresvirtual;
+	u32int	pitch;			/* returned by gpu */
+	u32int	bpp;
+	u32int	xoffset;
+	u32int	yoffset;
+	u32int	base;			/* returned by gpu */
+	u32int	screensize;		/* returned by gpu */
+};
+
+
+struct Prophdr {
+	u32int	len;
+	u32int	req;
+	u32int	tag;
+	u32int	tagbuflen;
+	u32int	taglen;
+	u32int	data[1];
+};
+
+static void
+vcwrite(uint chan, int val)
+{
+	u32int *r;
+
+	r = (u32int*)MAILBOX + NRegs;
+	val &= ~ChanMask;
+	while(r[Status]&Full)
+		;
+	coherence();
+	r[Write] = val | chan;
+}
+
+static int
+vcread(uint chan)
+{
+	u32int *r;
+	int x;
+
+	r = (u32int*)MAILBOX;
+	do{
+		while(r[Status]&Empty)
+			;
+		coherence();
+		x = r[Read];
+	}while((x&ChanMask) != chan);
+	return x & ~ChanMask;
+}
+
+/*
+ * Property interface
+ */
+
+static int
+vcreq(int tag, void *buf, int vallen, int rsplen)
+{
+	uintptr r;
+	int n;
+	Prophdr *prop;
+	static uintptr base = BUSDRAM;
+
+	if(rsplen < vallen)
+		rsplen = vallen;
+	rsplen = (rsplen+3) & ~3;
+	prop = (Prophdr*)(VCBUFFER);
+	n = sizeof(Prophdr) + rsplen + 8;
+	memset(prop, 0, n);
+	prop->len = n;
+	prop->req = Req;
+	prop->tag = tag;
+	prop->tagbuflen = rsplen;
+	prop->taglen = vallen;
+	if(vallen > 0)
+		memmove(prop->data, buf, vallen);
+	cachedwbinvse(prop, prop->len);
+	for(;;){
+		vcwrite(ChanProps, PADDR(prop) + base);
+		r = vcread(ChanProps);
+		if(r == PADDR(prop) + base)
+			break;
+		if(base == 0)
+			return -1;
+		base = 0;
+	}
+	if(prop->req == RspOk && prop->tag == tag && prop->taglen & TagResp) {
+		if((n = prop->taglen & ~TagResp) < rsplen)
+			rsplen = n;
+		memmove(buf, prop->data, rsplen);
+	}else
+		rsplen = -1;
+
+	return rsplen;
+}
+
+/*
+ * Framebuffer
+ */
+
+static int
+fbdefault(int *width, int *height, int *depth)
+{
+	u32int buf[3];
+
+	if(vcreq(TagGetres, &buf[0], 0, 2*4) != 2*4 ||
+	   vcreq(TagGetdepth, &buf[2], 0, 4) != 4)
+		return -1;
+	*width = buf[0];
+	*height = buf[1];
+	*depth = buf[2];
+	return 0;
+}
+
+void*
+fbinit(int set, int *width, int *height, int *depth)
+{
+	Fbinfo *fi;
+	uintptr va;
+
+	if(!set)
+		fbdefault(width, height, depth);
+	/* Screen width must be a multiple of 16 */
+	*width &= ~0xF;
+	fi = (Fbinfo*)(VCBUFFER);
+	memset(fi, 0, sizeof(*fi));
+	fi->xres = fi->xresvirtual = *width;
+	fi->yres = fi->yresvirtual = *height;
+	fi->bpp = *depth;
+	cachedwbinvse(fi, sizeof(*fi));
+	vcwrite(ChanFb, DMAADDR(fi));
+	if(vcread(ChanFb) != 0)
+		return 0;
+	va = mmukmap(FRAMEBUFFER, PADDR(fi->base), fi->screensize);
+	if(va)
+		memset((char*)va, 0x7F, fi->screensize);
+	return (void*)va;
+}
+
+int
+fbblank(int blank)
+{
+	u32int buf[1];
+
+	buf[0] = blank;
+	if(vcreq(TagFbblank, buf, sizeof buf, sizeof buf) != sizeof buf)
+		return -1;
+	return buf[0] & 1;
+}
+
+/*
+ * Power management
+ */
+void
+setpower(int dev, int on)
+{
+	u32int buf[2];
+
+	buf[0] = dev;
+	buf[1] = Powerwait | (on? 1: 0);
+	vcreq(TagSetpower, buf, sizeof buf, sizeof buf);
+}
+
+int
+getpower(int dev)
+{
+	u32int buf[2];
+
+	buf[0] = dev;
+	buf[1] = 0;
+	if(vcreq(TagGetpower, buf, sizeof buf[0], sizeof buf) != sizeof buf)
+		return -1;
+	return buf[0] & 1;
+}
+
+/*
+ * Get ethernet address (as hex string)
+ *	 [not reentrant]
+ */
+char *
+getethermac(void)
+{
+	uchar ea[8];
+	char *p;
+	int i;
+	static char buf[16];
+
+	memset(ea, 0, sizeof ea);
+	vcreq(TagGetmac, ea, 0, sizeof ea);
+	p = buf;
+	for(i = 0; i < 6; i++)
+		p += sprint(p, "%.2x", ea[i]);
+	return buf;
+}
+
+/*
+ * Get firmware revision
+ */
+uint
+getfirmware(void)
+{
+	u32int buf[1];
+
+	if(vcreq(TagGetfwrev, buf, 0, sizeof buf) != sizeof buf)
+		return 0;
+	return buf[0];
+}
+
+/*
+ * Get ARM ram
+ */
+void
+getramsize(Confmem *mem)
+{
+	u32int buf[2];
+
+	if(vcreq(TagGetram, buf, 0, sizeof buf) != sizeof buf)
+		return;
+	mem->base = buf[0];
+	mem->limit = buf[1];
+}
+
+/*
+ * Get clock rate
+ */
+ulong
+getclkrate(int clkid)
+{
+	u32int buf[2];
+
+	buf[0] = clkid;
+	if(vcreq(TagGetclkspd, buf, sizeof(buf[0]), sizeof(buf)) != sizeof buf)
+		return 0;
+	return buf[1];
+}
--- /dev/null
+++ b/sys/src/9/bcm/vfp3.c
@@ -1,0 +1,518 @@
+/*
+ * VFPv2 or VFPv3 floating point unit
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "arm.h"
+
+/* subarchitecture code in m->havefp */
+enum {
+	VFPv2	= 2,
+	VFPv3	= 3,
+};
+
+/* fp control regs.  most are read-only */
+enum {
+	Fpsid =	0,
+	Fpscr =	1,			/* rw */
+	Mvfr1 =	6,
+	Mvfr0 =	7,
+	Fpexc =	8,			/* rw */
+	Fpinst= 9,			/* optional, for exceptions */
+	Fpinst2=10,
+};
+enum {
+	/* Fpexc bits */
+	Fpex =		1u << 31,
+	Fpenabled =	1 << 30,
+	Fpdex =		1 << 29,	/* defined synch exception */
+//	Fp2v =		1 << 28,	/* Fpinst2 reg is valid */
+//	Fpvv =		1 << 27,	/* if Fpdex, vecitr is valid */
+//	Fptfv = 	1 << 26,	/* trapped fault is valid */
+//	Fpvecitr =	MASK(3) << 8,
+	/* FSR bits appear here */
+	Fpmbc =		Fpdex,		/* bits exception handler must clear */
+
+	/* Fpscr bits; see u.h for more */
+	Stride =	MASK(2) << 20,
+	Len =		MASK(3) << 16,
+	Dn=		1 << 25,
+	Fz=		1 << 24,
+	/* trap exception enables (not allowed in vfp3) */
+	FPIDNRM =	1 << 15,	/* input denormal */
+	Alltraps = FPIDNRM | FPINEX | FPUNFL | FPOVFL | FPZDIV | FPINVAL,
+	/* pending exceptions */
+	FPAIDNRM =	1 << 7,		/* input denormal */
+	Allexc = FPAIDNRM | FPAINEX | FPAUNFL | FPAOVFL | FPAZDIV | FPAINVAL,
+	/* condition codes */
+	Allcc =		MASK(4) << 28,
+};
+enum {
+	/* CpCPaccess bits */
+	Cpaccnosimd =	1u << 31,
+	Cpaccd16 =	1 << 30,
+};
+
+static char *
+subarch(int impl, uint sa)
+{
+	static char *armarchs[] = {
+		"VFPv1 (unsupported)",
+		"VFPv2",
+		"VFPv3+ with common VFP subarch v2",
+		"VFPv3+ with null subarch",
+		"VFPv3+ with common VFP subarch v3",
+	};
+
+	if (impl != 'A' || sa >= nelem(armarchs))
+		return "GOK";
+	else
+		return armarchs[sa];
+}
+
+static char *
+implement(uchar impl)
+{
+	if (impl == 'A')
+		return "arm";
+	else
+		return "unknown";
+}
+
+static int
+havefp(void)
+{
+	int gotfp;
+	ulong acc, sid;
+
+	if (m->havefpvalid)
+		return m->havefp;
+
+	m->havefp = 0;
+	gotfp = 1 << CpFP | 1 << CpDFP;
+	cpwrsc(0, CpCONTROL, 0, CpCPaccess, MASK(28));
+	acc = cprdsc(0, CpCONTROL, 0, CpCPaccess);
+	if ((acc & (MASK(2) << (2*CpFP))) == 0) {
+		gotfp &= ~(1 << CpFP);
+		print("fpon: no single FP coprocessor\n");
+	}
+	if ((acc & (MASK(2) << (2*CpDFP))) == 0) {
+		gotfp &= ~(1 << CpDFP);
+		print("fpon: no double FP coprocessor\n");
+	}
+	if (!gotfp) {
+		print("fpon: no FP coprocessors\n");
+		m->havefpvalid = 1;
+		return 0;
+	}
+	m->fpon = 1;			/* don't panic */
+	sid = fprd(Fpsid);
+	m->fpon = 0;
+	switch((sid >> 16) & MASK(7)){
+	case 0:				/* VFPv1 */
+		break;
+	case 1:				/* VFPv2 */
+		m->havefp = VFPv2;
+		m->fpnregs = 16;
+		break;
+	default:			/* VFPv3 or later */
+		m->havefp = VFPv3;
+		m->fpnregs = (acc & Cpaccd16) ? 16 : 32;
+		break;
+	}
+	if (m->machno == 0)
+		print("fp: %d registers, %s simd\n", m->fpnregs,
+			(acc & Cpaccnosimd? " no": ""));
+	m->havefpvalid = 1;
+	return 1;
+}
+
+/*
+ * these can be called to turn the fpu on or off for user procs,
+ * not just at system start up or shutdown.
+ */
+
+void
+fpoff(void)
+{
+	if (m->fpon) {
+		fpwr(Fpexc, 0);
+		m->fpon = 0;
+	}
+}
+
+void
+fpononly(void)
+{
+	if (!m->fpon && havefp()) {
+		/* enable fp.  must be first operation on the FPUs. */
+		fpwr(Fpexc, Fpenabled);
+		m->fpon = 1;
+	}
+}
+
+static void
+fpcfg(void)
+{
+	int impl;
+	ulong sid;
+	static int printed;
+
+	/* clear pending exceptions; no traps in vfp3; all v7 ops are scalar */
+	m->fpscr = Dn | Fz | FPRNR | (FPINVAL | FPZDIV | FPOVFL) & ~Alltraps;
+	fpwr(Fpscr, m->fpscr);
+	m->fpconfiged = 1;
+
+	if (printed)
+		return;
+	sid = fprd(Fpsid);
+	impl = sid >> 24;
+	print("fp: %s arch %s; rev %ld\n", implement(impl),
+		subarch(impl, (sid >> 16) & MASK(7)), sid & MASK(4));
+	printed = 1;
+}
+
+void
+fpinit(void)
+{
+	if (havefp()) {
+		fpononly();
+		fpcfg();
+	}
+}
+
+void
+fpon(void)
+{
+	if (havefp()) {
+	 	fpononly();
+		if (m->fpconfiged)
+			fpwr(Fpscr, (fprd(Fpscr) & Allcc) | m->fpscr);
+		else
+			fpcfg();	/* 1st time on this fpu; configure it */
+	}
+}
+
+void
+fpclear(void)
+{
+//	ulong scr;
+
+	fpon();
+//	scr = fprd(Fpscr);
+//	m->fpscr = scr & ~Allexc;
+//	fpwr(Fpscr, m->fpscr);
+
+	fpwr(Fpexc, fprd(Fpexc) & ~Fpmbc);
+}
+
+
+/*
+ * Called when a note is about to be delivered to a
+ * user process, usually at the end of a system call.
+ * Note handlers are not allowed to use the FPU so
+ * the state is marked (after saving if necessary) and
+ * checked in the Device Not Available handler.
+ */
+void
+fpunotify(Ureg*)
+{
+	if(up->fpstate == FPactive){
+		fpsave(&up->fpsave);
+		up->fpstate = FPinactive;
+	}
+	up->fpstate |= FPillegal;
+}
+
+/*
+ * Called from sysnoted() via the machine-dependent
+ * noted() routine.
+ * Clear the flag set above in fpunotify().
+ */
+void
+fpunoted(void)
+{
+	up->fpstate &= ~FPillegal;
+}
+
+/*
+ * Called early in the non-interruptible path of
+ * sysrfork() via the machine-dependent syscall() routine.
+ * Save the state so that it can be easily copied
+ * to the child process later.
+ */
+void
+fpusysrfork(Ureg*)
+{
+	if(up->fpstate == FPactive){
+		fpsave(&up->fpsave);
+		up->fpstate = FPinactive;
+	}
+}
+
+/*
+ * Called later in sysrfork() via the machine-dependent
+ * sysrforkchild() routine.
+ * Copy the parent FPU state to the child.
+ */
+void
+fpusysrforkchild(Proc *p, Ureg *, Proc *up)
+{
+	/* don't penalize the child, it hasn't done FP in a note handler. */
+	p->fpstate = up->fpstate & ~FPillegal;
+}
+
+/* should only be called if p->fpstate == FPactive */
+void
+fpsave(FPsave *fps)
+{
+	int n;
+
+	fpon();
+	fps->control = fps->status = fprd(Fpscr);
+	assert(m->fpnregs);
+	for (n = 0; n < m->fpnregs; n++)
+		fpsavereg(n, (uvlong *)fps->regs[n]);
+	fpoff();
+}
+
+static void
+fprestore(Proc *p)
+{
+	int n;
+
+	fpon();
+	fpwr(Fpscr, p->fpsave.control);
+	m->fpscr = fprd(Fpscr) & ~Allcc;
+	assert(m->fpnregs);
+	for (n = 0; n < m->fpnregs; n++)
+		fprestreg(n, *(uvlong *)p->fpsave.regs[n]);
+}
+
+/*
+ * Called from sched() and sleep() via the machine-dependent
+ * procsave() routine.
+ * About to go in to the scheduler.
+ * If the process wasn't using the FPU
+ * there's nothing to do.
+ */
+void
+fpuprocsave(Proc *p)
+{
+	if(p->fpstate == FPactive){
+		if(p->state == Moribund)
+			fpclear();
+		else{
+			/*
+			 * Fpsave() stores without handling pending
+			 * unmasked exeptions. Postnote() can't be called
+			 * here as sleep() already has up->rlock, so
+			 * the handling of pending exceptions is delayed
+			 * until the process runs again and generates an
+			 * emulation fault to activate the FPU.
+			 */
+			fpsave(&p->fpsave);
+		}
+		p->fpstate = FPinactive;
+	}
+}
+
+/*
+ * The process has been rescheduled and is about to run.
+ * Nothing to do here right now. If the process tries to use
+ * the FPU again it will cause a Device Not Available
+ * exception and the state will then be restored.
+ */
+void
+fpuprocrestore(Proc *)
+{
+}
+
+/*
+ * Disable the FPU.
+ * Called from sysexec() via sysprocsetup() to
+ * set the FPU for the new process.
+ */
+void
+fpusysprocsetup(Proc *p)
+{
+	p->fpstate = FPinit;
+	fpoff();
+}
+
+static void
+mathnote(void)
+{
+	ulong status;
+	char *msg, note[ERRMAX];
+
+	status = up->fpsave.status;
+
+	/*
+	 * Some attention should probably be paid here to the
+	 * exception masks and error summary.
+	 */
+	if (status & FPAINEX)
+		msg = "inexact";
+	else if (status & FPAOVFL)
+		msg = "overflow";
+	else if (status & FPAUNFL)
+		msg = "underflow";
+	else if (status & FPAZDIV)
+		msg = "divide by zero";
+	else if (status & FPAINVAL)
+		msg = "bad operation";
+	else
+		msg = "spurious";
+	snprint(note, sizeof note, "sys: fp: %s fppc=%#p status=%#lux",
+		msg, up->fpsave.pc, status);
+	postnote(up, 1, note, NDebug);
+}
+
+static void
+mathemu(Ureg *)
+{
+	if(m->havefp == VFPv3 && !(fprd(Fpexc) & (Fpex|Fpdex)))
+		iprint("mathemu: not an FP exception but an unknown FP opcode\n");
+	switch(up->fpstate){
+	case FPemu:
+		error("illegal instruction: VFP opcode in emulated mode");
+	case FPinit:
+		fpinit();
+		up->fpstate = FPactive;
+		break;
+	case FPinactive:
+		/*
+		 * Before restoring the state, check for any pending
+		 * exceptions.  There's no way to restore the state without
+		 * generating an unmasked exception.
+		 * More attention should probably be paid here to the
+		 * exception masks and error summary.
+		 */
+		if(up->fpsave.status & (FPAINEX|FPAUNFL|FPAOVFL|FPAZDIV|FPAINVAL)){
+			mathnote();
+			break;
+		}
+		fprestore(up);
+		up->fpstate = FPactive;
+		break;
+	case FPactive:
+		error("illegal instruction: bad vfp fpu opcode");
+		break;
+	}
+	fpclear();
+}
+
+void
+fpstuck(uintptr pc)
+{
+	if (m->fppc == pc && m->fppid == up->pid) {
+		m->fpcnt++;
+		if (m->fpcnt > 4)
+			panic("fpuemu: cpu%d stuck at pid %ld %s pc %#p "
+				"instr %#8.8lux", m->machno, up->pid, up->text,
+				pc, *(ulong *)pc);
+	} else {
+		m->fppid = up->pid;
+		m->fppc = pc;
+		m->fpcnt = 0;
+	}
+}
+
+enum {
+	N = 1<<31,
+	Z = 1<<30,
+	C = 1<<29,
+	V = 1<<28,
+	REGPC = 15,
+};
+
+static int
+condok(int cc, int c)
+{
+	switch(c){
+	case 0:	/* Z set */
+		return cc&Z;
+	case 1:	/* Z clear */
+		return (cc&Z) == 0;
+	case 2:	/* C set */
+		return cc&C;
+	case 3:	/* C clear */
+		return (cc&C) == 0;
+	case 4:	/* N set */
+		return cc&N;
+	case 5:	/* N clear */
+		return (cc&N) == 0;
+	case 6:	/* V set */
+		return cc&V;
+	case 7:	/* V clear */
+		return (cc&V) == 0;
+	case 8:	/* C set and Z clear */
+		return cc&C && (cc&Z) == 0;
+	case 9:	/* C clear or Z set */
+		return (cc&C) == 0 || cc&Z;
+	case 10:	/* N set and V set, or N clear and V clear */
+		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
+	case 11:	/* N set and V clear, or N clear and V set */
+		return (cc&(N|V))==N || (cc&(N|V))==V;
+	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
+		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
+	case 13:	/* Z set, or N set and V clear or N clear and V set */
+		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
+	case 14:	/* always */
+		return 1;
+	case 15:	/* never (reserved) */
+		return 0;
+	}
+	return 0;	/* not reached */
+}
+
+/* only called to deal with user-mode instruction faults */
+int
+fpuemu(Ureg* ureg)
+{
+	int s, nfp, cop, op;
+	uintptr pc;
+
+	if(waserror()){
+		postnote(up, 1, up->errstr, NDebug);
+		return 1;
+	}
+
+	if(up->fpstate & FPillegal)
+		error("floating point in note handler");
+
+	nfp = 0;
+	pc = ureg->pc;
+	validaddr(pc, 4, 0);
+	if(!condok(ureg->psr, *(ulong*)pc >> 28))
+		iprint("fpuemu: conditional instr shouldn't have got here\n");
+	op  = (*(ulong *)pc >> 24) & MASK(4);
+	cop = (*(ulong *)pc >>  8) & MASK(4);
+	if(m->fpon)
+		fpstuck(pc);		/* debugging; could move down 1 line */
+	if (ISFPAOP(cop, op)) {		/* old arm 7500 fpa opcode? */
+//		iprint("fpuemu: fpa instr %#8.8lux at %#p\n", *(ulong *)pc, pc);
+//		error("illegal instruction: old arm 7500 fpa opcode");
+		s = spllo();
+		if(waserror()){
+			splx(s);
+			nexterror();
+		}
+		nfp = fpiarm(ureg);	/* advances pc past emulated instr(s) */
+		if (nfp > 1)		/* could adjust this threshold */
+			m->fppc = m->fpcnt = 0;
+		splx(s);
+		poperror();
+	} else if (ISVFPOP(cop, op)) {	/* if vfp, fpu must be off */
+		mathemu(ureg);		/* enable fpu & retry */
+		nfp = 1;
+	}
+
+	poperror();
+	return nfp;
+}
--- /dev/null
+++ b/sys/src/9/bcm/words
@@ -1,0 +1,149 @@
+raspberry pi
+
+broadcom 2835 SoC (based on 2708)
+arm1176jzf-s (v6 arch) 700MHz cpu, apparently dual-issue, with vfp2
+videocore 4 gpu
+
+l1 I & D VIPT caches
+	16K each: 4-way, 128 sets, 32-byte lines
+	l1 D is write-through, l1 I is write-back
+unified l2 PIPT cache 128K: 4-way?, 1024? sets, 32-byte lines, mostly for gpu
+(by default CPU doesn't see it)
+
+we arrange that device register accesses are uncached.
+
+256MB of dram at physical address 0, shared with gpu
+non-16550 uart for console
+	uart serial voltages are wrong (3.3v but rs232 is nominally 12v);
+	could use usb serial (ick).
+there's no real ethernet controller, so we have to use usb ether,
+and the usb controller is nastier than usual.
+
+There's a serial port (115200b/s) on P1 connector pins (GND,TXD,RXD) =
+(6,8,10).  These are 3v TTL signals: use a level-shifter to convert to
+RS232, or a USB-to-TTL-serial adapter.  Add the line "console=0
+b115200" to the /cfg/pxe file on the server, or the parameter
+"console='0 b115200'" to cmdline.txt on the SD card.
+
+9pi is a Plan 9 terminal, which can boot with local fossil root on the
+sd card (/dev/sdM0), or with root from a Plan 9 file server via tcp.
+
+9picpu is a Plan 9 cpu server, which could be used in a headless
+configuration without screen, keyboard or mouse.
+
+9pifat is a minimal configuration which boots a shell script boot.rc
+with root in /plan9 on the dos partition, maybe useful for embedded
+applications where a full Plan 9 system is not needed.
+
+Network booting with u-boot:
+start with a normal rpi u-boot sd (e.g. raspberry-pi-uboot-20120707).
+update the start.elf with a version from a newer rpi distro (see below).
+mk installall
+add new system to ndb
+see booting(8)
+
+Booting from sd card:
+- start with a normal rpi distro sd (e.g. 2012-08-16-wheezy-raspbian)
+  [NB: versions of start.elf earlier than this may not be compatible]
+- copy 9pi to sd's root directory
+- add or change "kernel=" line in config.txt to "kernel=9pi"
+- plan9.ini is built from the "kernel arguments" in cmdline.txt - each
+  var=value entry becomes one plan9.ini line, so entries with spaces will
+  need single quotes.
+
+
+	physical mem map
+
+hex addr size	what
+----
+0	 256MB	sdram, cached
+00000000 64	exception vectors
+00000100 7936	boot ATAGs (inc. cmdline.txt)
+00002000 4K	Mach
+00003000 1K	L2 page table for exception vectors
+00003400 1K	videocore mailbox buffer
+00003800 2K	FIQ stack
+00004000 16K	L1 page table for kernel
+00008000 -	default kernel load address
+01000000 16K	u-boot env
+20000000 16M	peripherals
+20003000	system timer(s)
+20007000	dma
+2000B000	arm control: intr, timers 0 & 1, semas, doorbells, mboxes
+20100000	power, reset, watchdog
+20200000	gpio
+20201000	uart0
+20202000	mmc
+20215040	uart1 (mini uart)
+20300000	eMMC
+20600000	smi
+20980000	otg usb
+
+40000000	l2 cache only
+7e00b000	arm control
+7e2000c0	jtag
+7e201000?	pl011 usrt
+7e215000	aux: uart1, spi[12]
+
+80000000
+
+c0000000	bypass caches
+
+	virtual mem map (from cpu address map & mmu mappings)
+
+hex addr size	what
+----
+0	 512MB	user process address space
+7e000000 16M	i/o registers
+80000000 <=224M	kernel ram (reserve some for GPU)
+c0000000 256MB	kzero, mapped to 0
+ffff0000 4K	exception vectors
+
+Linux params at *R2 (default 0x100) are a sequence of ATAGs
+  struct atag {
+	u32int size;		/* size of ATAG in words, including header */
+	u32int tag;		/* ATAG_CORE is first, ATAG_NONE is last */
+	u32int data[size-2];
+  };
+00000000	ATAG_NONE
+54410001	ATAG_CORE
+54410002	ATAG_MEM
+54410009	ATAG_CMDLINE
+
+uart dmas	15, 14
+
+intrs (96)
+irq1
+0	timer0
+1	timer1
+2	timer2
+3	timer3
+8	isp
+9	usb
+16	dma0
+17	dma1
+⋯
+28	dma12
+29	aux: uart1
+30	arm
+31	vpu dma
+
+irq2
+35	sdc
+36	dsio
+40	hdmi0
+41	hdmi1
+48	smi
+56	sdio
+57	uart1 aka "vc uart"
+
+irq0
+64	timer
+65	mbox
+66	doorbell0
+67	doorbell1
+75	usb
+77	dma2
+78	dma3
+82	sdio
+83	uart0