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), ®(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(®(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