diff options
author | cinap_lenrek <cinap_lenrek@gmx.de> | 2013-01-26 17:33:56 +0100 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@gmx.de> | 2013-01-26 17:33:56 +0100 |
commit | bc610a1b1c32f6e2e9b034217bb3ce9a7defa739 (patch) | |
tree | 98fa1e680867eaf19c1be5e818a570713fa7a286 /sys/src/9/bcm | |
parent | ea108c8ca6e726ac008f75775ab83775ec233171 (diff) |
add raspberry pi kernel (from sources)
Diffstat (limited to 'sys/src/9/bcm')
40 files changed, 8295 insertions, 0 deletions
diff --git a/sys/src/9/bcm/arch.c b/sys/src/9/bcm/arch.c new file mode 100644 index 000000000..22d63fb43 --- /dev/null +++ b/sys/src/9/bcm/arch.c @@ -0,0 +1 @@ +#include "../omap/arch.c" diff --git a/sys/src/9/bcm/archbcm.c b/sys/src/9/bcm/archbcm.c new file mode 100644 index 000000000..eb9c4c2d8 --- /dev/null +++ b/sys/src/9/bcm/archbcm.c @@ -0,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) +{ +} + diff --git a/sys/src/9/bcm/arm.h b/sys/src/9/bcm/arm.h new file mode 100644 index 000000000..534f42d29 --- /dev/null +++ b/sys/src/9/bcm/arm.h @@ -0,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 diff --git a/sys/src/9/bcm/arm.s b/sys/src/9/bcm/arm.s new file mode 100644 index 000000000..2dbb8778a --- /dev/null +++ b/sys/src/9/bcm/arm.s @@ -0,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) diff --git a/sys/src/9/bcm/clock.c b/sys/src/9/bcm/clock.c new file mode 100644 index 000000000..42d9a7b04 --- /dev/null +++ b/sys/src/9/bcm/clock.c @@ -0,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); +} diff --git a/sys/src/9/bcm/coproc.c b/sys/src/9/bcm/coproc.c new file mode 100644 index 000000000..87efa65b4 --- /dev/null +++ b/sys/src/9/bcm/coproc.c @@ -0,0 +1 @@ +#include "../teg2/coproc.c" diff --git a/sys/src/9/bcm/dat.h b/sys/src/9/bcm/dat.h new file mode 100644 index 000000000..cb860a6fa --- /dev/null +++ b/sys/src/9/bcm/dat.h @@ -0,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 */ +}; + diff --git a/sys/src/9/bcm/devarch.c b/sys/src/9/bcm/devarch.c new file mode 100644 index 000000000..3ef0cdc08 --- /dev/null +++ b/sys/src/9/bcm/devarch.c @@ -0,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); +} diff --git a/sys/src/9/bcm/devfakertc.c b/sys/src/9/bcm/devfakertc.c new file mode 100644 index 000000000..ee8112fd9 --- /dev/null +++ b/sys/src/9/bcm/devfakertc.c @@ -0,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, +}; + diff --git a/sys/src/9/bcm/dma.c b/sys/src/9/bcm/dma.c new file mode 100644 index 000000000..0a071ca00 --- /dev/null +++ b/sys/src/9/bcm/dma.c @@ -0,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; +} diff --git a/sys/src/9/bcm/dwcotg.h b/sys/src/9/bcm/dwcotg.h new file mode 100644 index 000000000..545cb80ed --- /dev/null +++ b/sys/src/9/bcm/dwcotg.h @@ -0,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, +}; diff --git a/sys/src/9/bcm/emmc.c b/sys/src/9/bcm/emmc.c new file mode 100644 index 000000000..fd6b1e50e --- /dev/null +++ b/sys/src/9/bcm/emmc.c @@ -0,0 +1,426 @@ +/* + * bcm2835 external mass media controller (mmc / sd host interface) + * + * Copyright © 2012 Richard Miller <r.miller@acm.org> + */ + +#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, +}; diff --git a/sys/src/9/bcm/fns.h b/sys/src/9/bcm/fns.h new file mode 100644 index 000000000..92615d9ed --- /dev/null +++ b/sys/src/9/bcm/fns.h @@ -0,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 */ diff --git a/sys/src/9/bcm/fpi.c b/sys/src/9/bcm/fpi.c new file mode 100644 index 000000000..103d01031 --- /dev/null +++ b/sys/src/9/bcm/fpi.c @@ -0,0 +1 @@ +#include "../omap/fpi.c" diff --git a/sys/src/9/bcm/fpi.h b/sys/src/9/bcm/fpi.h new file mode 100644 index 000000000..97dfe895a --- /dev/null +++ b/sys/src/9/bcm/fpi.h @@ -0,0 +1 @@ +#include "../omap/fpi.h" diff --git a/sys/src/9/bcm/fpiarm.c b/sys/src/9/bcm/fpiarm.c new file mode 100644 index 000000000..571e89fa6 --- /dev/null +++ b/sys/src/9/bcm/fpiarm.c @@ -0,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; +} diff --git a/sys/src/9/bcm/fpimem.c b/sys/src/9/bcm/fpimem.c new file mode 100644 index 000000000..c8f568b5d --- /dev/null +++ b/sys/src/9/bcm/fpimem.c @@ -0,0 +1 @@ +#include "../omap/fpimem.c" diff --git a/sys/src/9/bcm/init9.s b/sys/src/9/bcm/init9.s new file mode 100644 index 000000000..751ad84fc --- /dev/null +++ b/sys/src/9/bcm/init9.s @@ -0,0 +1 @@ +#include "../omap/init9.s" diff --git a/sys/src/9/bcm/io.h b/sys/src/9/bcm/io.h new file mode 100644 index 000000000..0bf103670 --- /dev/null +++ b/sys/src/9/bcm/io.h @@ -0,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) diff --git a/sys/src/9/bcm/l.s b/sys/src/9/bcm/l.s new file mode 100644 index 000000000..6c716350f --- /dev/null +++ b/sys/src/9/bcm/l.s @@ -0,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 diff --git a/sys/src/9/bcm/lexception.s b/sys/src/9/bcm/lexception.s new file mode 100644 index 000000000..d9653ecd7 --- /dev/null +++ b/sys/src/9/bcm/lexception.s @@ -0,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 diff --git a/sys/src/9/bcm/lproc.s b/sys/src/9/bcm/lproc.s new file mode 100644 index 000000000..4fe095d2a --- /dev/null +++ b/sys/src/9/bcm/lproc.s @@ -0,0 +1 @@ +#include "../omap/lproc.s" diff --git a/sys/src/9/bcm/main.c b/sys/src/9/bcm/main.c new file mode 100644 index 000000000..4a00d1b13 --- /dev/null +++ b/sys/src/9/bcm/main.c @@ -0,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); +} diff --git a/sys/src/9/bcm/mem.h b/sys/src/9/bcm/mem.h new file mode 100644 index 000000000..7bd97ca89 --- /dev/null +++ b/sys/src/9/bcm/mem.h @@ -0,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) diff --git a/sys/src/9/bcm/mkfile b/sys/src/9/bcm/mkfile new file mode 100644 index 000000000..5966bf358 --- /dev/null +++ b/sys/src/9/bcm/mkfile @@ -0,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 diff --git a/sys/src/9/bcm/mmu.c b/sys/src/9/bcm/mmu.c new file mode 100644 index 000000000..8eba6e1d3 --- /dev/null +++ b/sys/src/9/bcm/mmu.c @@ -0,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); +} + diff --git a/sys/src/9/bcm/mouse.c b/sys/src/9/bcm/mouse.c new file mode 100644 index 000000000..56f9a948a --- /dev/null +++ b/sys/src/9/bcm/mouse.c @@ -0,0 +1 @@ +#include "../omap/mouse.c" diff --git a/sys/src/9/bcm/picpuf b/sys/src/9/bcm/picpuf new file mode 100644 index 000000000..0faa53d34 --- /dev/null +++ b/sys/src/9/bcm/picpuf @@ -0,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 diff --git a/sys/src/9/bcm/pif b/sys/src/9/bcm/pif new file mode 100644 index 000000000..99059f492 --- /dev/null +++ b/sys/src/9/bcm/pif @@ -0,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 diff --git a/sys/src/9/bcm/rebootcode.s b/sys/src/9/bcm/rebootcode.s new file mode 100644 index 000000000..8e924ccfd --- /dev/null +++ b/sys/src/9/bcm/rebootcode.s @@ -0,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 diff --git a/sys/src/9/bcm/screen.c b/sys/src/9/bcm/screen.c new file mode 100644 index 000000000..e96fa4fdf --- /dev/null +++ b/sys/src/9/bcm/screen.c @@ -0,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; + } +} diff --git a/sys/src/9/bcm/screen.h b/sys/src/9/bcm/screen.h new file mode 100644 index 000000000..8703796a9 --- /dev/null +++ b/sys/src/9/bcm/screen.h @@ -0,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 */ diff --git a/sys/src/9/bcm/softfpu.c b/sys/src/9/bcm/softfpu.c new file mode 100644 index 000000000..6ea32a807 --- /dev/null +++ b/sys/src/9/bcm/softfpu.c @@ -0,0 +1 @@ +#include "../teg2/softfpu.c" diff --git a/sys/src/9/bcm/syscall.c b/sys/src/9/bcm/syscall.c new file mode 100644 index 000000000..259c3f895 --- /dev/null +++ b/sys/src/9/bcm/syscall.c @@ -0,0 +1 @@ +#include "../kw/syscall.c" diff --git a/sys/src/9/bcm/trap.c b/sys/src/9/bcm/trap.c new file mode 100644 index 000000000..b6bf34389 --- /dev/null +++ b/sys/src/9/bcm/trap.c @@ -0,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); +} diff --git a/sys/src/9/bcm/uartmini.c b/sys/src/9/bcm/uartmini.c new file mode 100644 index 000000000..aa9b87f13 --- /dev/null +++ b/sys/src/9/bcm/uartmini.c @@ -0,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); +} diff --git a/sys/src/9/bcm/usbdwc.c b/sys/src/9/bcm/usbdwc.c new file mode 100644 index 000000000..272405537 --- /dev/null +++ b/sys/src/9/bcm/usbdwc.c @@ -0,0 +1,965 @@ +/* + * USB host driver for BCM2835 + * Synopsis DesignWare Core USB 2.0 OTG controller + * + * Copyright © 2012 Richard Miller <r.miller@acm.org> + * + * 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); +} diff --git a/sys/src/9/bcm/vcore.c b/sys/src/9/bcm/vcore.c new file mode 100644 index 000000000..8ee811f8b --- /dev/null +++ b/sys/src/9/bcm/vcore.c @@ -0,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]; +} diff --git a/sys/src/9/bcm/vfp3.c b/sys/src/9/bcm/vfp3.c new file mode 100644 index 000000000..e87cf1d32 --- /dev/null +++ b/sys/src/9/bcm/vfp3.c @@ -0,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; +} diff --git a/sys/src/9/bcm/words b/sys/src/9/bcm/words new file mode 100644 index 000000000..e1b31cd79 --- /dev/null +++ b/sys/src/9/bcm/words @@ -0,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 |