Commits

mischief committed 1b61f86 Draft

import miller's raspberry pi port of plan 9 from contrib

  • Participants
  • Parent commits e3a4296

Comments (0)

Files changed (50)

 case -f* -r* -c* -m*
 	exec upas/nedmail $*
 case *
-	exec upas/marshal -8 $*
+	exec upas/marshal $*
 }

sys/src/9/port/sd.h

 typedef struct SDev SDev;
 typedef struct SDfile SDfile;
 typedef struct SDifc SDifc;
+typedef struct SDio SDio;
 typedef struct SDpart SDpart;
 typedef struct SDperm SDperm;
 typedef struct SDreq SDreq;
 enum {
 	SDnosense	= 0x00000001,
 	SDvalidsense	= 0x00010000,
+
+	SDinq0periphqual= 0xe0,
+	SDinq0periphtype= 0x1f,
+	SDinq1removable	= 0x80,
+
+	/* periphtype values */
+	SDperdisk	= 0,	/* Direct access (disk) */
+	SDpertape	= 1,	/* Sequential eg, tape */
+	SDperpr		= 2,	/* Printer */
+	SDperworm	= 4,	/* Worm */
+	SDpercd		= 5,	/* CD-ROM */
+	SDpermo		= 7,	/* rewriteable MO */
+	SDperjuke	= 8,	/* medium-changer */
 };
 
 enum {
 	SDcdb		= 2,
 };
 
+#ifndef sdmalloc
 #define sdmalloc(n)	malloc(n)
 #define sdfree(p)	free(p)
+#endif
+
+/*
+ * mmc/sd/sdio host controller interface
+ */
+
+struct SDio {
+	char	*name;
+	int	(*init)(void);
+	void	(*enable)(void);
+	int	(*inquiry)(char*, int);
+	int	(*cmd)(u32int, u32int, u32int*);
+	void	(*iosetup)(int, void*, int, int);
+	void	(*io)(int, uchar*, int);
+};
+
+extern SDio sdio;
 
 /* devsd.c */
 extern void sdadddevs(SDev*);

sys/src/9/port/sdmmc.c

+/*
+ * mmc / sd memory card
+ *
+ * Copyright © 2012 Richard Miller <r.miller@acm.org>
+ *
+ * Assumes only one card on the bus
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "../port/sd.h"
+
+#define CSD(end, start)	rbits(csd, start, (end)-(start)+1)
+
+typedef struct Ctlr Ctlr;
+
+enum {
+	Inittimeout	= 15,
+	Multiblock	= 1,
+
+	/* Commands */
+	GO_IDLE_STATE	= 0,
+	ALL_SEND_CID	= 2,
+	SEND_RELATIVE_ADDR= 3,
+	SELECT_CARD	= 7,
+	SD_SEND_IF_COND	= 8,
+	SEND_CSD	= 9,
+	STOP_TRANSMISSION= 12,
+	SEND_STATUS	= 13,
+	SET_BLOCKLEN	= 16,
+	READ_SINGLE_BLOCK= 17,
+	READ_MULTIPLE_BLOCK= 18,
+	WRITE_BLOCK	= 24,
+	WRITE_MULTIPLE_BLOCK= 25,
+	APP_CMD		= 55,	/* prefix for following app-specific commands */
+	SET_BUS_WIDTH	= 6,
+	SD_SEND_OP_COND	= 41,
+
+	/* Command arguments */
+	/* SD_SEND_IF_COND */
+	Voltage		= 1<<8,
+	Checkpattern	= 0x42,
+
+	/* SELECT_CARD */
+	Rcashift	= 16,
+
+	/* SD_SEND_OP_COND */
+	Hcs	= 1<<30,	/* host supports SDHC & SDXC */
+	Ccs	= 1<<30,	/* card is SDHC or SDXC */
+	V3_3	= 3<<20,	/* 3.2-3.4 volts */
+
+	/* SET_BUS_WIDTH */
+	Width1	= 0<<0,
+	Width4	= 2<<0,
+
+	/* OCR (operating conditions register) */
+	Powerup	= 1<<31,
+};
+
+struct Ctlr {
+	SDev	*dev;
+	SDio	*io;
+	/* SD card registers */
+	u16int	rca;
+	u32int	ocr;
+	u32int	cid[4];
+	u32int	csd[4];
+};
+
+extern SDifc sdmmcifc;
+extern SDio sdio;
+
+static uint
+rbits(u32int *p, uint start, uint len)
+{
+	uint w, off, v;
+
+	w   = start / 32;
+	off = start % 32;
+	if(off == 0)
+		v = p[w];
+	else
+		v = p[w] >> off | p[w+1] << (32-off);
+	if(len < 32)
+		return v & ((1<<len) - 1);
+	else
+		return v;
+}
+
+static void
+identify(SDunit *unit, u32int *csd)
+{
+	uint csize, mult;
+
+	unit->secsize = 1 << CSD(83, 80);
+	switch(CSD(127, 126)){
+	case 0:				/* CSD version 1 */
+		csize = CSD(73, 62);
+		mult = CSD(49, 47);
+		unit->sectors = (csize+1) * (1<<(mult+2));
+		break;
+	case 1:				/* CSD version 2 */
+		csize = CSD(69, 48);
+		unit->sectors = (csize+1) * 512LL*KiB / unit->secsize;
+		break;
+	}
+	if(unit->secsize == 1024){
+		unit->sectors <<= 1;
+		unit->secsize = 512;
+	}
+}
+
+static SDev*
+mmcpnp(void)
+{
+	SDev *sdev;
+	Ctlr *ctl;
+
+	if(sdio.init() < 0)
+		return nil;
+	sdev = malloc(sizeof(SDev));
+	if(sdev == nil)
+		return nil;
+	ctl = malloc(sizeof(Ctlr));
+	if(ctl == nil){
+		free(sdev);
+		return nil;
+	}
+	sdev->idno = 'M';
+	sdev->ifc = &sdmmcifc;
+	sdev->nunit = 1;
+	sdev->ctlr = ctl;
+	ctl->dev = sdev;
+	ctl->io = &sdio;
+	return sdev;
+}
+
+static int
+mmcverify(SDunit *unit)
+{
+	int n;
+	Ctlr *ctl;
+
+	ctl = unit->dev->ctlr;
+	n = ctl->io->inquiry((char*)&unit->inquiry[8], sizeof(unit->inquiry)-8);
+	if(n < 0)
+		return 0;
+	unit->inquiry[0] = SDperdisk;
+	unit->inquiry[1] = SDinq1removable;
+	unit->inquiry[4] = sizeof(unit->inquiry)-4;
+	return 1;
+}
+
+static int
+mmcenable(SDev* dev)
+{
+	Ctlr *ctl;
+
+	ctl = dev->ctlr;
+	ctl->io->enable();
+	return 1;
+}
+
+static int
+mmconline(SDunit *unit)
+{
+	int hcs, i;
+	u32int r[4];
+	Ctlr *ctl;
+	SDio *io;
+
+	ctl = unit->dev->ctlr;
+	io = ctl->io;
+	assert(unit->subno == 0);
+
+	if(waserror()){
+		unit->sectors = 0;
+		return 0;
+	}
+	if(unit->sectors != 0){
+		io->cmd(SEND_STATUS, ctl->rca<<Rcashift, r);
+		poperror();
+		return 1;
+	}
+	io->cmd(GO_IDLE_STATE, 0, r);
+	hcs = 0;
+	if(!waserror()){
+		io->cmd(SD_SEND_IF_COND, Voltage|Checkpattern, r);
+		if(r[0] == (Voltage|Checkpattern))	/* SD 2.0 or above */
+			hcs = Hcs;
+		poperror();
+	}
+	for(i = 0; i < Inittimeout; i++){
+		delay(100);
+		io->cmd(APP_CMD, 0, r);
+		io->cmd(SD_SEND_OP_COND, hcs|V3_3, r);
+		if(r[0] & Powerup)
+			break;
+	}
+	if(i == Inittimeout){
+		print("sdmmc: card won't power up\n");
+		poperror();
+		return 2;
+	}
+	ctl->ocr = r[0];
+	io->cmd(ALL_SEND_CID, 0, r);
+	memmove(ctl->cid, r, sizeof ctl->cid);
+	io->cmd(SEND_RELATIVE_ADDR, 0, r);
+	ctl->rca = r[0]>>16;
+	io->cmd(SEND_CSD, ctl->rca<<Rcashift, r);
+	memmove(ctl->csd, r, sizeof ctl->csd);
+	identify(unit, ctl->csd);
+	io->cmd(SELECT_CARD, ctl->rca<<Rcashift, r);
+	io->cmd(SET_BLOCKLEN, unit->secsize, r);
+	io->cmd(APP_CMD, ctl->rca<<Rcashift, r);
+	io->cmd(SET_BUS_WIDTH, Width4, r);
+	poperror();
+	return 1;
+}
+
+static int
+mmcrctl(SDunit *unit, char *p, int l)
+{
+	Ctlr *ctl;
+	int i, n;
+
+	ctl = unit->dev->ctlr;
+	assert(unit->subno == 0);
+	if(unit->sectors == 0){
+		mmconline(unit);
+		if(unit->sectors == 0)
+			return 0;
+	}
+	n = snprint(p, l, "rca %4.4ux ocr %8.8ux\ncid ", ctl->rca, ctl->ocr);
+	for(i = nelem(ctl->cid)-1; i >= 0; i--)
+		n += snprint(p+n, l-n, "%8.8ux", ctl->cid[i]);
+	n += snprint(p+n, l-n, " csd ");
+	for(i = nelem(ctl->csd)-1; i >= 0; i--)
+		n += snprint(p+n, l-n, "%8.8ux", ctl->csd[i]);
+	n += snprint(p+n, l-n, "\ngeometry %llud %ld\n",
+		unit->sectors, unit->secsize);
+	return n;
+}
+
+static long
+mmcbio(SDunit *unit, int lun, int write, void *data, long nb, uvlong bno)
+{
+	int len, tries;
+	ulong b;
+	u32int r[4];
+	uchar *buf;
+	Ctlr *ctl;
+	SDio *io;
+
+	USED(lun);
+	ctl = unit->dev->ctlr;
+	io = ctl->io;
+	assert(unit->subno == 0);
+	if(unit->sectors == 0)
+		error("media change");
+	buf = data;
+	len = unit->secsize;
+	if(Multiblock){
+		b = bno;
+		tries = 0;
+		while(waserror())
+			if(++tries == 3)
+				nexterror();
+		io->iosetup(write, buf, len, nb);
+		if(waserror()){
+			io->cmd(STOP_TRANSMISSION, 0, r);
+			nexterror();
+		}
+		io->cmd(write? WRITE_MULTIPLE_BLOCK: READ_MULTIPLE_BLOCK,
+			ctl->ocr & Ccs? b: b * len, r);
+		io->io(write, buf, nb * len);
+		poperror();
+		io->cmd(STOP_TRANSMISSION, 0, r);
+		poperror();
+		b += nb;
+	}else{
+		for(b = bno; b < bno + nb; b++){
+			io->iosetup(write, buf, len, 1);
+			io->cmd(write? WRITE_BLOCK : READ_SINGLE_BLOCK,
+				ctl->ocr & Ccs? b: b * len, r);
+			io->io(write, buf, len);
+			buf += len;
+		}
+	}
+	return (b - bno) * len;
+}
+
+static int
+mmcrio(SDreq*)
+{
+	return -1;
+}
+
+SDifc sdmmcifc = {
+	.name	= "mmc",
+	.pnp	= mmcpnp,
+	.enable	= mmcenable,
+	.verify	= mmcverify,
+	.online	= mmconline,
+	.rctl	= mmcrctl,
+	.bio	= mmcbio,
+	.rio	= mmcrio,
+};

sys/src/9/rpi/README

+Plan 9 for raspberry pi (bcm2835).
+
+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 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.
+
+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 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.
+
+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 above)
+- install 9pi on bootp host; e.g. for Plan 9, add to ndb/local:
+	sys=pi dom=pi ip=N.N.N.N ether=NNNNNNNNNNNN bootf=/sys/src/9/bcm/9pi
+- u-boot commands for network boot, with plan9.ini file from bootp host:
+	usb start; dhcp; tftp 100 %C; go 8000
+
+If you want a serial console, 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 plan9.ini file on the server,
+or the parameter "console='0 b115200'" to cmdline.txt on the SD card.
+
+

sys/src/9/rpi/arch.c

+#include "../omap/arch.c"

sys/src/9/rpi/archbcm.c

+/*
+ * 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"
+
+#include "../port/netif.h"
+#include "etherif.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
+archreboot(void)
+{
+	u32int *r;
+
+	r = (u32int*)POWERREGS;
+	r[Wdog] = Password | 1;
+	r[Rstc] = Password | (r[Rstc] & ~CfgMask) | CfgReset;
+	coherence();
+	for(;;)
+		;
+}
+
+void
+cpuidprint(void)
+{
+	iprint("cpu%d: %dMHz ARM1176JZF-S\n", m->machno, m->cpumhz);
+}
+
+void
+archbcmlink(void)
+{
+}
+
+int
+archether(unsigned ctlrno, Ether *ether)
+{
+	switch(ctlrno) {
+	case 0:
+		ether->type = "usb";
+		ether->ctlrno = ctlrno;
+		ether->irq = -1;
+		ether->nopt = 0;
+		ether->mbps = 100;
+		return 1;
+	}
+	return -1;
+}
+

sys/src/9/rpi/arm.h

+/*
+ * 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 */
+
+/*
+ * Coprocessors
+ */
+#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 */
+
+/*
+ * 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 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

sys/src/9/rpi/arm.s

+/*
+ * armv6 machine assist, definitions
+ *
+ * loader uses R11 as scratch.
+ */
+
+#include "mem.h"
+#include "arm.h"
+
+#define PADDR(va)	(PHYSDRAM | ((va) & ~KSEGM))
+
+/*
+ * 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)

sys/src/9/rpi/boot.rc

+#!/boot/rc -m /boot/rcmain
+
+/boot/echo -n boot...
+path=(/bin /boot)
+bind '#p' /proc
+bind '#d' /fd
+bind -a '#P' /dev
+bind -a '#t' /dev
+bind -a '#S' /dev
+bind -a '#I' /net
+echo -n rpi >/dev/hostowner
+echo -n fdisk...
+fdisk -p /dev/sdM0/data >/dev/sdM0/ctl
+dossrv -f/dev/sdM0/dos boot
+rootdir=/root/plan9
+rootspec=''
+mount -c /srv/boot /root
+bind -ac $rootdir /
+bind -ac $rootdir/mnt /mnt
+
+bind /$cputype/bin /bin
+bind -a /rc/bin /bin
+
+if (! ~ $#init 0)
+	exec `{echo $init}
+if (~ $service cpu)
+	exec /$cputype/init -c
+if not
+	exec /$cputype/init -t
+exec /boot/rc -m/boot/rcmain -i

sys/src/9/rpi/clock.c

+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ * 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()
+ */
+
+#define	SYSTIMERS	(VIRTIO+0x3000)
+#define	ARMTIMER	(VIRTIO+0xB400)
+
+enum {
+	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
+clockinit(void)
+{
+	Systimers *tn;
+	Armtimer *tm;
+	u32int t0, t1;
+	u32int tstart, tend;
+
+	tn = (Systimers*)SYSTIMERS;
+	tm = (Armtimer*)ARMTIMER;
+	tm->load = 0;
+	tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
+
+	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;
+	}
+}
+
+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);
+}

sys/src/9/rpi/dat.h

+/*
+ * 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;
+};
+
+/*
+ * emulated floating point
+ */
+struct FPsave
+{
+	ulong	status;
+	ulong	control;
+	ulong	regs[8][3];
+
+	int	fpstate;
+};
+
+/*
+ * FPsave.status
+ */
+enum
+{
+	FPinit,
+	FPactive,
+	FPinactive,
+
+	/* bit 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 */
+
+	/* 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 */
+};
+

sys/src/9/rpi/devarch.c

+#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);
+}

sys/src/9/rpi/devether.c

+#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/netif.h"
+#include "etherif.h"
+
+extern int archether(unsigned ctlno, Ether *ether);
+
+static Ether *etherxx[MaxEther];
+
+Chan*
+etherattach(char* spec)
+{
+	int ctlrno;
+	char *p;
+	Chan *chan;
+
+	ctlrno = 0;
+	if(spec && *spec){
+		ctlrno = strtoul(spec, &p, 0);
+		if((ctlrno == 0 && p == spec) || *p != 0)
+			error(Ebadarg);
+		if(ctlrno < 0 || ctlrno >= MaxEther)
+			error(Ebadarg);
+	}
+	if(etherxx[ctlrno] == 0)
+		error(Enodev);
+
+	chan = devattach('l', spec);
+	if(waserror()){
+		chanfree(chan);
+		nexterror();
+	}
+	chan->dev = ctlrno;
+	if(etherxx[ctlrno]->attach)
+		etherxx[ctlrno]->attach(etherxx[ctlrno]);
+	poperror();
+	return chan;
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan* nchan, char** name, int nname)
+{
+	return netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+	return netifstat(etherxx[chan->dev], chan, dp, n);
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+	return netifopen(etherxx[chan->dev], chan, omode);
+}
+
+static Chan *
+ethercreate(Chan* ch, char*, int, ulong)
+{
+	return ch;
+}
+
+static void
+etherclose(Chan* chan)
+{
+	netifclose(etherxx[chan->dev], chan);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+	Ether *ether;
+	ulong offset = off;
+
+	ether = etherxx[chan->dev];
+	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+		/*
+		 * With some controllers it is necessary to reach
+		 * into the chip to extract statistics.
+		 */
+		if(NETTYPE(chan->qid.path) == Nifstatqid)
+			return ether->ifstat(ether, buf, n, offset);
+		else if(NETTYPE(chan->qid.path) == Nstatqid)
+			ether->ifstat(ether, buf, 0, offset);
+	}
+
+	return netifread(ether, chan, buf, n, offset);
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+	return netifbread(etherxx[chan->dev], chan, n, offset);
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+	return netifwstat(etherxx[chan->dev], chan, dp, n);
+}
+
+static void
+etherrtrace(Netfile* f, Etherpkt* pkt, int len)
+{
+	int i, n;
+	Block *bp;
+
+	if(qwindow(f->in) <= 0)
+		return;
+	if(len > 58)
+		n = 58;
+	else
+		n = len;
+	bp = iallocb(64);
+	if(bp == nil)
+		return;
+	memmove(bp->wp, pkt->d, n);
+	i = TK2MS(MACHP(0)->ticks);
+	bp->wp[58] = len>>8;
+	bp->wp[59] = len;
+	bp->wp[60] = i>>24;
+	bp->wp[61] = i>>16;
+	bp->wp[62] = i>>8;
+	bp->wp[63] = i;
+	bp->wp += 64;
+	qpass(f->in, bp);
+}
+
+Block*
+etheriq(Ether* ether, Block* bp, int fromwire)
+{
+	Etherpkt *pkt;
+	ushort type;
+	int len, multi, tome, fromme;
+	Netfile **ep, *f, **fp, *fx;
+	Block *xbp;
+
+	ether->inpackets++;
+
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	type = (pkt->type[0]<<8)|pkt->type[1];
+	fx = 0;
+	ep = &ether->f[Ntypes];
+
+	multi = pkt->d[0] & 1;
+	/* check for valid multicast addresses */
+	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 &&
+	    ether->prom == 0){
+		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+			if(fromwire){
+				freeb(bp);
+				bp = 0;
+			}
+			return bp;
+		}
+	}
+	/* is it for me? */
+	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+	/*
+	 * Multiplex the packet to all the connections which want it.
+	 * If the packet is not to be used subsequently (fromwire != 0),
+	 * attempt to simply pass it into one of the connections, thereby
+	 * saving a copy of the data (usual case hopefully).
+	 */
+	for(fp = ether->f; fp < ep; fp++){
+		if((f = *fp) != nil && (f->type == type || f->type < 0) &&
+		    (tome || multi || f->prom)){
+			/* Don't want to hear bridged packets */
+			if(f->bridge && !fromwire && !fromme)
+				continue;
+			if(!f->headersonly){
+				if(fromwire && fx == 0)
+					fx = f;
+				else if(xbp = iallocb(len)){
+					memmove(xbp->wp, pkt, len);
+					xbp->wp += len;
+					if(qpass(f->in, xbp) < 0)
+						ether->soverflows++;
+				}
+				else
+					ether->soverflows++;
+			}
+			else
+				etherrtrace(f, pkt, len);
+		}
+	}
+
+	if(fx){
+		if(qpass(fx->in, bp) < 0)
+			ether->soverflows++;
+		return 0;
+	}
+	if(fromwire){
+		freeb(bp);
+		return 0;
+	}
+	return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+	int len, loopback, s;
+	Etherpkt *pkt;
+
+	ether->outpackets++;
+
+	/*
+	 * Check if the packet has to be placed back onto the input queue,
+	 * i.e. if it's a loopback or broadcast packet or the interface is
+	 * in promiscuous mode.
+	 * If it's a loopback packet indicate to etheriq that the data isn't
+	 * needed and return, etheriq will pass-on or free the block.
+	 * To enable bridging to work, only packets that were originated
+	 * by this interface are fed back.
+	 */
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+		s = splhi();
+		etheriq(ether, bp, 0);
+		splx(s);
+	}
+
+	if(!loopback){
+		qbwrite(ether->oq, bp);
+		if(ether->transmit != nil)
+			ether->transmit(ether);
+	} else
+		freeb(bp);
+
+	return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+	Ether *ether;
+	Block *bp;
+	int nn, onoff;
+	Cmdbuf *cb;
+
+	ether = etherxx[chan->dev];
+	if(NETTYPE(chan->qid.path) != Ndataqid) {
+		nn = netifwrite(ether, chan, buf, n);
+		if(nn >= 0)
+			return nn;
+		cb = parsecmd(buf, n);
+		if(cb->f[0] && strcmp(cb->f[0], "nonblocking") == 0){
+			if(cb->nf <= 1)
+				onoff = 1;
+			else
+				onoff = atoi(cb->f[1]);
+			qnoblock(ether->oq, onoff);
+			free(cb);
+			return n;
+		}
+		free(cb);
+		if(ether->ctl!=nil)
+			return ether->ctl(ether,buf,n);
+			
+		error(Ebadctl);
+	}
+
+	if(n > ether->maxmtu)
+		error(Etoobig);
+	if(n < ether->minmtu)
+		error(Etoosmall);
+
+	bp = allocb(n);
+	if(waserror()){
+		freeb(bp);
+		nexterror();
+	}
+	memmove(bp->rp, buf, n);
+	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+	poperror();
+	bp->wp += n;
+
+	return etheroq(ether, bp);
+}
+
+static long
+etherbwrite(Chan* chan, Block* bp, ulong)
+{
+	Ether *ether;
+	long n;
+
+	n = BLEN(bp);
+	if(NETTYPE(chan->qid.path) != Ndataqid){
+		if(waserror()) {
+			freeb(bp);
+			nexterror();
+		}
+		n = etherwrite(chan, bp->rp, n, 0);
+		poperror();
+		freeb(bp);
+		return n;
+	}
+	ether = etherxx[chan->dev];
+
+	if(n > ether->maxmtu){
+		freeb(bp);
+		error(Etoobig);
+	}
+	if(n < ether->minmtu){
+		freeb(bp);
+		error(Etoosmall);
+	}
+
+	return etheroq(ether, bp);
+}
+
+static struct {
+	char*	type;
+	int	(*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+	static int ncard;
+
+	if(ncard == MaxEther)
+		panic("too many ether cards");
+	cards[ncard].type = t;
+	cards[ncard].reset = r;
+	ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < Eaddrlen; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+static void
+etherreset(void)
+{
+	Ether *ether;
+	int i, n, ctlrno;
+	char name[KNAMELEN], buf[128];
+
+	for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		if(ether == 0)
+			ether = malloc(sizeof(Ether));
+		memset(ether, 0, sizeof(Ether));
+		ether->ctlrno = ctlrno;
+		ether->mbps = 10;
+		ether->minmtu = ETHERMINTU;
+		ether->maxmtu = ETHERMAXTU;
+
+		if(archether(ctlrno, ether) <= 0)
+			continue;
+
+		//if(isaconfig("ether", ctlrno, ether) == 0){
+		//	free(ether);
+//			return nil;
+		//	continue;
+		//}
+		for(n = 0; cards[n].type; n++){
+			if(cistrcmp(cards[n].type, ether->type))
+				continue;
+			for(i = 0; i < ether->nopt; i++)
+				if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
+					if(parseether(ether->ea,
+					    &ether->opt[i][3]) == -1)
+						memset(ether->ea, 0, Eaddrlen);
+				} else if(cistrcmp(ether->opt[i],
+				    "100BASE-TXFD") == 0)
+					ether->mbps = 100;
+			if(cards[n].reset(ether))
+				break;
+			snprint(name, sizeof(name), "ether%d", ctlrno);
+
+			if(ether->interrupt != nil && ether->irq >= 0)
+				intrenable(ether->irq, ether->interrupt,
+					ether, 0, name);
+
+			i = snprint(buf, sizeof buf,
+				"#l%d: %s: %dMbps port %#lux irq %d",
+				ctlrno, ether->type, ether->mbps, ether->port,
+				ether->irq);
+			if(ether->mem)
+				i += snprint(buf+i, sizeof buf - i,
+					" addr %#lux", PADDR(ether->mem));
+			if(ether->size)
+				i += snprint(buf+i, sizeof buf - i,
+					" size %#luX", ether->size);
+			i += snprint(buf+i, sizeof buf - i,
+				": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
+				ether->ea[0], ether->ea[1], ether->ea[2],
+				ether->ea[3], ether->ea[4], ether->ea[5]);
+			snprint(buf+i, sizeof buf - i, "\n");
+			iprint("%s", buf);  /* it may be too early for print */
+
+			if(ether->mbps >= 1000)
+				netifinit(ether, name, Ntypes, 4*1024*1024);
+			else if(ether->mbps >= 100)
+				netifinit(ether, name, Ntypes, 1024*1024);
+			else
+				netifinit(ether, name, Ntypes, 65*1024);
+			if(ether->oq == 0)
+				ether->oq = qopen(ether->limit, Qmsg, 0, 0);
+			if(ether->oq == 0)
+				panic("etherreset %s", name);
+			ether->alen = Eaddrlen;
+			memmove(ether->addr, ether->ea, Eaddrlen);
+			memset(ether->bcast, 0xFF, Eaddrlen);
+
+			etherxx[ctlrno] = ether;
+			ether = 0;
+			break;
+		}
+	}
+	if(ether)
+		free(ether);
+}
+
+static void
+ethershutdown(void)
+{
+	Ether *ether;
+	int i;
+
+	for(i = 0; i < MaxEther; i++){
+		ether = etherxx[i];
+		if(ether == nil)
+			continue;
+		if(ether->shutdown == nil) {
+			print("#l%d: no shutdown function\n", i);
+			continue;
+		}
+		(*ether->shutdown)(ether);
+	}
+}
+
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+	int i, j;
+	ulong crc, b;
+
+	crc = 0xffffffff;
+	for(i = 0; i < len; i++){
+		b = *p++;
+		for(j = 0; j < 8; j++){
+			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+			b >>= 1;
+		}
+	}
+	return crc;
+}
+
+void
+dumpoq(Queue *oq)
+{
+	if (oq == nil)
+		print("no outq! ");
+	else if (qisclosed(oq))
+		print("outq closed ");
+	else if (qfull(oq))
+		print("outq full ");
+	else
+		print("outq %d ", qlen(oq));
+}
+
+void
+dumpnetif(Netif *netif)
+{
+	print("netif %s ", netif->name);
+	print("limit %d mbps %d link %d ",
+		netif->limit, netif->mbps, netif->link);
+	print("inpkts %lld outpkts %lld errs %d\n",
+		netif->inpackets, netif->outpackets,
+		netif->crcs + netif->oerrs + netif->frames + netif->overflows +
+		netif->buffs + netif->soverflows);
+}
+
+Dev etherdevtab = {
+	'l',
+	"ether",
+
+	etherreset,
+	devinit,
+	ethershutdown,
+	etherattach,
+	etherwalk,
+	etherstat,
+	etheropen,
+	ethercreate,
+	etherclose,
+	etherread,
+	etherbread,
+	etherwrite,
+	etherbwrite,
+	devremove,
+	etherwstat,
+};

sys/src/9/rpi/devfakertc.c

+/*
+ * 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,
+};
+

sys/src/9/rpi/devusb.c

+/*
+ * USB device driver framework.
+ *
+ * This is in charge of providing access to actual HCIs
+ * and providing I/O to the various endpoints of devices.
+ * A separate user program (usbd) is in charge of
+ * enumerating the bus, setting up endpoints and
+ * starting devices (also user programs).
+ *
+ * The interface provided is a violation of the standard:
+ * you're welcome.
+ *
+ * The interface consists of a root directory with several files
+ * plus a directory (epN.M) with two files per endpoint.
+ * A device is represented by its first endpoint, which
+ * is a control endpoint automatically allocated for each device.