diff -Nru /n/sources/plan9/sys/src/9/bcm/arch.c /sys/src/9/bcm/arch.c --- /n/sources/plan9/sys/src/9/bcm/arch.c Thu Jul 26 11:52:43 2012 +++ /sys/src/9/bcm/arch.c Tue May 31 00:00:00 2016 @@ -1 +1,191 @@ -#include "../omap/arch.c" +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include +#include "ureg.h" + +#include "arm.h" + +/* + * A lot of this stuff doesn't belong here + * but this is a convenient dumping ground for + * later sorting into the appropriate buckets. + */ + +/* Give enough context in the ureg to produce a kernel stack for + * a sleeping process + */ +void +setkernur(Ureg* ureg, Proc* p) +{ + ureg->pc = p->sched.pc; + ureg->sp = p->sched.sp+4; + ureg->r14 = PTR2UINT(sched); +} + +/* + * called in syscallfmt.c, sysfile.c, sysproc.c + */ +void +validalign(uintptr addr, unsigned align) +{ + /* + * Plan 9 is a 32-bit O/S, and the hardware it runs on + * does not usually have instructions which move 64-bit + * quantities directly, synthesizing the operations + * with 32-bit move instructions. Therefore, the compiler + * (and hardware) usually only enforce 32-bit alignment, + * if at all. + * + * Take this out if the architecture warrants it. + */ + if(align == sizeof(vlong)) + align = sizeof(long); + + /* + * Check align is a power of 2, then addr alignment. + */ + if((align != 0 && !(align & (align-1))) && !(addr & (align-1))) + return; + postnote(up, 1, "sys: odd address", NDebug); + error(Ebadarg); + /*NOTREACHED*/ +} + +/* go to user space */ +void +kexit(Ureg*) +{ + uvlong t; + Tos *tos; + + /* precise time accounting, kernel exit */ + tos = (Tos*)(USTKTOP-sizeof(Tos)); + cycles(&t); + tos->kcycles += t - up->kentry; + tos->pcycles = up->pcycles; + tos->cyclefreq = m->cpuhz; + tos->pid = up->pid; + + /* make visible immediately to user proc */ + cachedwbinvse(tos, sizeof *tos); +} + +/* + * return the userpc the last exception happened at + */ +uintptr +userpc(void) +{ + Ureg *ureg = up->dbgreg; + return ureg->pc; +} + +/* This routine must save the values of registers the user is not permitted + * to write from devproc and then restore the saved values before returning. + */ +void +setregisters(Ureg* ureg, char* pureg, char* uva, int n) +{ + USED(ureg, pureg, uva, n); +} + +/* + * this is the body for all kproc's + */ +static void +linkproc(void) +{ + spllo(); + up->kpfun(up->kparg); + pexit("kproc exiting", 0); +} + +/* + * setup stack and initial PC for a new kernel proc. This is architecture + * dependent because of the starting stack location + */ +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = PTR2UINT(linkproc); + p->sched.sp = PTR2UINT(p->kstack+KSTACK); + + p->kpfun = func; + p->kparg = arg; +} + +/* + * pc output by dumpaproc + */ +uintptr +dbgpc(Proc* p) +{ + Ureg *ureg; + + ureg = p->dbgreg; + if(ureg == 0) + return 0; + + return ureg->pc; +} + +/* + * set mach dependent process state for a new process + */ +void +procsetup(Proc* p) +{ + fpusysprocsetup(p); +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc* p) +{ + uvlong t; + + cycles(&t); + p->pcycles += t; + +// TODO: save and restore VFPv3 FP state once 5[cal] know the new registers. + fpuprocsave(p); + /* + * Prevent the following scenario: + * pX sleeps on cpuA, leaving its page tables in mmul1 + * pX wakes up on cpuB, and exits, freeing its page tables + * pY on cpuB allocates a freed page table page and overwrites with data + * cpuA takes an interrupt, and is now running with bad page tables + * In theory this shouldn't hurt because only user address space tables + * are affected, and mmuswitch will clear mmul1 before a user process is + * dispatched. But empirically it correlates with weird problems, eg + * resetting of the core clock at 0x4000001C which confuses local timers. + */ + if(conf.nmach > 1) + mmuswitch(nil); +} + +void +procrestore(Proc* p) +{ + uvlong t; + + if(p->kp) + return; + cycles(&t); + p->pcycles -= t; + + fpuprocrestore(p); +} + +int +userureg(Ureg* ureg) +{ + return (ureg->psr & PsrMask) == PsrMusr; +} diff -Nru /n/sources/plan9/sys/src/9/bcm/archbcm.c /sys/src/9/bcm/archbcm.c --- /n/sources/plan9/sys/src/9/bcm/archbcm.c Thu Feb 21 21:22:37 2013 +++ /sys/src/9/bcm/archbcm.c Tue May 31 00:00:00 2016 @@ -1,5 +1,5 @@ /* - * bcm2835 (e.g. raspberry pi) architecture-specific stuff + * bcm2835 (e.g. original raspberry pi) architecture-specific stuff */ #include "u.h" @@ -16,9 +16,19 @@ #define POWERREGS (VIRTIO+0x100000) +Soc soc = { + .dramsize = 512*MiB, + .physio = 0x20000000, + .busdram = 0x40000000, + .busio = 0x7E000000, + .armlocal = 0, + .l1ptedramattrs = Cached | Buffered, + .l2ptedramattrs = Cached | Buffered, +}; + enum { Wdogfreq = 65536, - Wdogtime = 5, /* seconds, ≤ 15 */ + Wdogtime = 10, /* seconds, ≤ 15 */ }; /* @@ -52,7 +62,7 @@ ; } -static void +void wdogfeed(void) { u32int *r; @@ -71,10 +81,33 @@ r[Rstc] = Password | (r[Rstc] & ~CfgMask); } +char * +cputype2name(char *buf, int size) +{ + seprint(buf, buf + size, "1176JZF-S"); + return buf; +} + void cpuidprint(void) { - print("cpu%d: %dMHz ARM1176JZF-S\n", m->machno, m->cpumhz); + char name[64]; + + cputype2name(name, sizeof name); + delay(50); /* let uart catch up */ + print("cpu%d: %dMHz ARM %s\n", m->machno, m->cpumhz, name); +} + +int +getncpus(void) +{ + return 1; +} + +int +startcpus(uint) +{ + return 1; } void @@ -86,15 +119,84 @@ int archether(unsigned ctlrno, Ether *ether) { - switch(ctlrno) { - case 0: - ether->type = "usb"; - ether->ctlrno = ctlrno; - ether->irq = -1; - ether->nopt = 0; - ether->mbps = 100; - return 1; - } - return -1; + ether->type = "usb"; + ether->ctlrno = ctlrno; + ether->irq = -1; + ether->nopt = 0; + return 1; +} + +int +l2ap(int ap) +{ + return (AP(3, (ap))|AP(2, (ap))|AP(1, (ap))|AP(0, (ap))); +} + +/* + * atomic ops + * make sure that we don't drag in the C library versions + */ + +long +_xdec(long *p) +{ + int s, v; + + s = splhi(); + v = --*p; + splx(s); + return v; +} + +void +_xinc(long *p) +{ + int s; + + s = splhi(); + ++*p; + splx(s); +} + +int +ainc(int *p) +{ + int s, v; + + s = splhi(); + v = ++*p; + splx(s); + return v; +} + +int +adec(int *p) +{ + int s, v; + + s = splhi(); + v = --*p; + splx(s); + return v; +} + +int +cas32(void* addr, u32int old, u32int new) +{ + int r, s; + + s = splhi(); + if(r = (*(u32int*)addr == old)) + *(u32int*)addr = new; + splx(s); + if (r) + coherence(); + return r; +} + +int +cmpswap(long *addr, long old, long new) +{ + return cas32(addr, old, new); } diff -Nru /n/sources/plan9/sys/src/9/bcm/archbcm2.c /sys/src/9/bcm/archbcm2.c --- /n/sources/plan9/sys/src/9/bcm/archbcm2.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/archbcm2.c Tue May 31 00:00:00 2016 @@ -0,0 +1,261 @@ +/* + * bcm2836 (e.g.raspberry pi 2) architecture-specific stuff + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" +#include "arm.h" + +#include "../port/netif.h" +#include "etherif.h" + +typedef struct Mbox Mbox; +typedef struct Mboxes Mboxes; + +#define POWERREGS (VIRTIO+0x100000) + +Soc soc = { + .dramsize = 0x3F000000, /* was 1024*MiB, but overlaps with physio */ + .physio = 0x3F000000, + .busdram = 0xC0000000, + .busio = 0x7E000000, + .armlocal = 0x40000000, + .l1ptedramattrs = Cached | Buffered | L1wralloc | L1sharable, + .l2ptedramattrs = Cached | Buffered | L2wralloc | L2sharable, +}; + +enum { + Wdogfreq = 65536, + Wdogtime = 10, /* seconds, ≤ 15 */ +}; + +/* + * Power management / watchdog registers + */ +enum { + Rstc = 0x1c>>2, + Password = 0x5A<<24, + CfgMask = 0x03<<4, + CfgReset = 0x02<<4, + Rsts = 0x20>>2, + Wdog = 0x24>>2, +}; + +/* + * Arm local regs for smp + */ +struct Mbox { + u32int doorbell; + u32int mbox1; + u32int mbox2; + u32int startcpu; +}; +struct Mboxes { + Mbox set[4]; + Mbox clr[4]; +}; + +enum { + Mboxregs = 0x80 +}; + +static Lock startlock[MAXMACH + 1]; + +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 +wdogfeed(void) +{ + u32int *r; + + r = (u32int*)POWERREGS; + r[Wdog] = Password | (Wdogtime * Wdogfreq); + r[Rstc] = Password | (r[Rstc] & ~CfgMask) | CfgReset; +} + +void +wdogoff(void) +{ + u32int *r; + + r = (u32int*)POWERREGS; + r[Rstc] = Password | (r[Rstc] & ~CfgMask); +} + + +char * +cputype2name(char *buf, int size) +{ + u32int r; + uint part; + char *p; + + r = cpidget(); /* main id register */ + assert((r >> 24) == 'A'); + part = (r >> 4) & MASK(12); + switch(part){ + case 0xc07: + p = seprint(buf, buf + size, "Cortex-A7"); + break; + case 0xd03: + p = seprint(buf, buf + size, "Cortex-A53"); + break; + default: + p = seprint(buf, buf + size, "Unknown-%#x", part); + break; + } + seprint(p, buf + size, " r%ldp%ld", + (r >> 20) & MASK(4), r & MASK(4)); + return buf; +} + +void +cpuidprint(void) +{ + char name[64]; + + cputype2name(name, sizeof name); + delay(50); /* let uart catch up */ + print("cpu%d: %dMHz ARM %s\n", m->machno, m->cpumhz, name); +} + +int +getncpus(void) +{ + int n, max; + char *p; + + n = 4; + if(n > MAXMACH) + n = MAXMACH; + p = getconf("*ncpu"); + if(p && (max = atoi(p)) > 0 && n > max) + n = max; + return n; +} + +static int +startcpu(uint cpu) +{ + Mboxes *mb; + int i; + void cpureset(); + + mb = (Mboxes*)(ARMLOCAL + Mboxregs); + if(mb->clr[cpu].startcpu) + return -1; + mb->set[cpu].startcpu = PADDR(cpureset); + for(i = 0; i < 1000; i++) + if(mb->clr[cpu].startcpu == 0) + return 0; + mb->clr[cpu].startcpu = PADDR(cpureset); + mb->set[cpu].doorbell = 1; + return 0; +} + +void +mboxclear(uint cpu) +{ + Mboxes *mb; + + mb = (Mboxes*)(ARMLOCAL + Mboxregs); + mb->clr[cpu].mbox1 = 1; +} + +void +wakecpu(uint cpu) +{ + Mboxes *mb; + + mb = (Mboxes*)(ARMLOCAL + Mboxregs); + mb->set[cpu].mbox1 = 1; +} + +int +startcpus(uint ncpu) +{ + int i; + + for(i = 0; i < ncpu; i++) + lock(&startlock[i]); + cachedwbse(startlock, sizeof startlock); + for(i = 1; i < ncpu; i++){ + if(startcpu(i) < 0) + return i; + lock(&startlock[i]); + unlock(&startlock[i]); + } + return ncpu; +} + +void +archbcm2link(void) +{ + addclock0link(wdogfeed, HZ); +} + +int +archether(unsigned ctlrno, Ether *ether) +{ + ether->type = "usb"; + ether->ctlrno = ctlrno; + ether->irq = -1; + ether->nopt = 0; + return 1; +} + +int +l2ap(int ap) +{ + return (AP(0, (ap))); +} + +int +cmpswap(long *addr, long old, long new) +{ + return cas((ulong*)addr, old, new); +} + +void +cpustart(int cpu) +{ + Mboxes *mb; + void machon(int); + + up = nil; + machinit(); + mb = (Mboxes*)(ARMLOCAL + Mboxregs); + mb->clr[cpu].doorbell = 1; + trapinit(); + clockinit(); + mmuinit1(); + timersinit(); + cpuidprint(); + archreset(); + machon(m->machno); + unlock(&startlock[cpu]); + schedinit(); + panic("schedinit returned"); +} diff -Nru /n/sources/plan9/sys/src/9/bcm/arm.h /sys/src/9/bcm/arm.h --- /n/sources/plan9/sys/src/9/bcm/arm.h Mon Jan 7 20:45:31 2013 +++ /sys/src/9/bcm/arm.h Tue May 31 00:00:00 2016 @@ -1,5 +1,5 @@ /* - * arm-specific definitions for armv6 + * arm-specific definitions for armv6 (arm11), armv7 (cortex-a8 and -a7) * these are used in C and assembler */ @@ -12,6 +12,7 @@ #define PsrMsvc 0x00000013 /* `protected mode for OS' */ #define PsrMmon 0x00000016 /* `secure monitor' (trustzone hyper) */ #define PsrMabt 0x00000017 +#define PsrMhyp 0x0000001A #define PsrMund 0x0000001B #define PsrMsys 0x0000001F /* `privileged user mode for OS' (trustzone) */ #define PsrMask 0x0000001F @@ -52,9 +53,19 @@ #define CpTLD 10 /* TLB Lockdown, with op2 */ #define CpVECS 12 /* vector bases, op1==0, Crm==0, op2s (cortex) */ #define CpPID 13 /* Process ID */ +#define CpTIMER 14 /* Generic timer (cortex-a7) */ #define CpSPM 15 /* system performance monitor (arm1176) */ /* + * CpTIMER op1==0 Crm and opcode2 registers (cortex-a7) + */ +#define CpTIMERcntfrq 0 +#define CpTIMERphys 2 + +#define CpTIMERphysval 0 +#define CpTIMERphysctl 1 + +/* * CpTTB op1==0, Crm==0 opcode2 values. */ #define CpTTB0 0 @@ -71,6 +82,7 @@ * CpID Secondary (CRm) registers. */ #define CpIDidct 0 +#define CpIDfeat 1 /* * CpID op1==0 opcode2 fields. @@ -80,6 +92,7 @@ #define CpIDct 1 /* cache type */ #define CpIDtlb 3 /* tlb type (cortex) */ #define CpIDmpid 5 /* multiprocessor id (cortex) */ +#define CpIDrevid 6 /* extra revision ID */ /* CpIDid op1 values */ #define CpIDcsize 1 /* cache size (cortex) */ @@ -133,6 +146,10 @@ #define CpACasa (1<<4) /* enable speculative accesses */ #define CpACl1pe (1<<3) /* l1 cache parity enable */ #define CpACl2en (1<<1) /* l2 cache enable; default 1 */ + +/* cortex-a7 and cortex-a9 */ +#define CpACsmp (1<<6) /* SMP l1 caches coherence; needed for ldrex/strex */ +#define CpACl1pctl (3<<13) /* l1 prefetch control */ /* * CpCONTROL Secondary (CRm) registers and opcode2 fields. */ @@ -151,9 +168,9 @@ #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 CpCACHEwb 10 /* writeback to PoC */ +#define CpCACHEwbu 11 /* writeback to PoU */ +#define CpCACHEwbi 14 /* writeback+invalidate (to PoC) */ #define CpCACHEall 0 /* entire (not for invd nor wb(i) on cortex) */ #define CpCACHEse 1 /* single entry */ @@ -223,7 +240,7 @@ #define CpVECSmon 1 /* secure monitor base addr */ /* - * CpSPM Secondary (CRm) registers and opcode2 fields. + * CpSPM Secondary (CRm) registers and opcode2 fields (armv6) */ #define CpSPMperf 12 /* various counters */ @@ -239,6 +256,21 @@ #define CpCACHERANGEdwbi 14 /* writeback+invalidate */ /* + * CpTTB cache control bits + */ +#define CpTTBnos (1<<5) /* only Inner cache shareable */ +#define CpTTBinc (0<<0|0<<6) /* inner non-cacheable */ +#define CpTTBiwba (0<<0|1<<6) /* inner write-back write-allocate */ +#define CpTTBiwt (1<<0|0<<6) /* inner write-through */ +#define CpTTBiwb (1<<0|1<<6) /* inner write-back no write-allocate */ +#define CpTTBonc (0<<3) /* outer non-cacheable */ +#define CpTTBowba (1<<3) /* outer write-back write-allocate */ +#define CpTTBowt (2<<3) /* outer write-through */ +#define CpTTBowb (3<<3) /* outer write-back no write-allocate */ +#define CpTTBs (1<<1) /* page table in shareable memory */ +#define CpTTBbase ~0x7F /* mask off control bits */ + +/* * MMU page table entries. * Mbz (0x10) bit is implementation-defined and must be 0 on the cortex. */ @@ -256,6 +288,15 @@ #define Cached 0x00000008 /* L[12] */ #define Dom0 0 +#define L1wralloc (1<<12) /* L1 TEX */ +#define L1sharable (1<<16) +#define L2wralloc (1<<6) /* L2 TEX (small pages) */ +#define L2sharable (1<<10) + +/* attributes for memory containing locks -- differs between armv6 and armv7 */ +//#define L1ptedramattrs (Cached | Buffered | L1wralloc | L1sharable) +//#define L2ptedramattrs (Cached | Buffered | L2wralloc | L2sharable) + #define Noaccess 0 /* AP, DAC */ #define Krw 1 /* AP */ /* armv7 deprecates AP[2] == 1 & AP[1:0] == 2 (Uro), prefers 3 (new in v7) */ @@ -267,7 +308,7 @@ #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 */ +/* L2AP differs between armv6 and armv7 -- see l2ap in arch*.c */ #define DAC(n, v) F((v), (n)*2, 2) #define HVECTORS 0xffff0000 diff -Nru /n/sources/plan9/sys/src/9/bcm/arm.s /sys/src/9/bcm/arm.s --- /n/sources/plan9/sys/src/9/bcm/arm.s Fri Dec 28 16:31:55 2012 +++ /sys/src/9/bcm/arm.s Tue May 31 00:00:00 2016 @@ -1,5 +1,5 @@ /* - * armv6 machine assist, definitions + * armv6/v7 machine assist, definitions * * loader uses R11 as scratch. */ @@ -11,8 +11,6 @@ #define L1X(va) (((((va))>>20) & 0x0fff)<<2) -#define PTEDRAM (Dom0|L1AP(Krw)|Section|Cached|Buffered) - /* * new instructions */ @@ -25,12 +23,37 @@ MOVW $0, R0; \ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait -#define BARRIERS ISB; DSB +#define BARRIERS DSB; ISB #define MCRR(coproc, op, rd, rn, crm) \ WORD $(0xec400000|(rn)<<16|(rd)<<12|(coproc)<<8|(op)<<4|(crm)) +#define MRRC(coproc, op, rd, rn, crm) \ + WORD $(0xec500000|(rn)<<16|(rd)<<12|(coproc)<<8|(op)<<4|(crm)) +#define MSR(R, rn, m, m1) \ + WORD $(0xe120f200|(R)<<22|(m1)<<16|(m)<<8|(rn)) + +#define LDREX(fp,t) WORD $(0xe<<28|0x01900f9f | (fp)<<16 | (t)<<12) +/* `The order of operands is from left to right in dataflow order' - asm man */ +#define STREX(f,tp,r) WORD $(0xe<<28|0x01800f90 | (tp)<<16 | (r)<<12 | (f)<<0) +#define CLREX WORD $0xf57ff01f + +#define CPSIE WORD $0xf1080080 /* intr enable: zeroes I bit */ +#define CPSID WORD $0xf10c0080 /* intr disable: sets I bit */ #define OKAY \ MOVW $0x7E200028,R2; \ MOVW $0x10000,R3; \ MOVW R3,(R2) + +#define PUTC(s) + +/* + * get cpu id, or zero if armv6 + */ +#define CPUID(r) \ + MRC CpSC, 0, r, C(CpID), C(CpIDfeat), 7; \ + CMP $0, r; \ + B.EQ 2(PC); \ + MRC CpSC, 0, r, C(CpID), C(CpIDidct), CpIDmpid; \ + AND.S $(MAXMACH-1), r + diff -Nru /n/sources/plan9/sys/src/9/bcm/armv6.s /sys/src/9/bcm/armv6.s --- /n/sources/plan9/sys/src/9/bcm/armv6.s Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/armv6.s Tue May 31 00:00:00 2016 @@ -0,0 +1,324 @@ +/* + * Broadcom bcm2835 SoC, as used in Raspberry Pi + * arm1176jzf-s processor (armv6) + */ + +#include "arm.s" + +#define CACHELINESZ 32 + +TEXT armstart(SB), 1, $-4 + + /* + * 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 + MOVW $PADDR(L1), R0 + 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|CpCpredict|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 cpidget(SB), 1, $-4 /* main ID */ + MRC CpSC, 0, R0, C(CpID), C(0), CpIDid + RET + +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 + MOVW CPSR, R3 + ORR $(PsrDirq|PsrDfiq), R3, R1 /* splfhi */ + MOVW R1, CPSR + + DSB + MOVW nrdy(SB), R0 + CMP $0, R0 + MCR.EQ CpSC, 0, R0, C(CpCACHE), C(CpCACHEintr), CpCACHEwait + DSB + + 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 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEflushbtc + 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 data cache + */ +TEXT cachedwb(SB), 1, $-4 + DSB + MOVW $0, R0 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEall + 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 cachedwbtlb(SB), 1, $-4 +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 + +/* + * cachedinvse(va, n) + * drain write buffer + * invalidate data cache range [va, va+n) + */ +TEXT cachedinvse(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, CpCACHERANGEinvd) + 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 + +/* + * L2 cache is not enabled + */ +TEXT l2cacheuwbinv(SB), 1, $-4 + RET + +/* + * invalidate instruction cache + */ +TEXT cacheiinv(SB), 1, $-4 + MOVW $0, R0 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall + RET + +/* + * invalidate range of instruction cache + */ +TEXT cacheiinvse(SB), 1, $-4 + MOVW R0, R1 /* DSB clears R0 */ + DSB + MOVW n+4(FP), R2 + ADD R1, R2 + SUB $1, R2 + MCRR(CpSC, 0, 2, 1, CpCACHERANGEinvi) + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEflushbtc + DSB + ISB + RET diff -Nru /n/sources/plan9/sys/src/9/bcm/armv7.s /sys/src/9/bcm/armv7.s --- /n/sources/plan9/sys/src/9/bcm/armv7.s Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/armv7.s Tue May 31 00:00:00 2016 @@ -0,0 +1,486 @@ +/* + * Broadcom bcm2836 SoC, as used in Raspberry Pi 2 + * 4 x Cortex-A7 processor (armv7) + */ + +#include "arm.s" + +#define CACHELINESZ 64 +#define ICACHELINESZ 32 + +#undef DSB +#undef DMB +#undef ISB +#define DSB WORD $0xf57ff04f /* data synch. barrier; last f = SY */ +#define DMB WORD $0xf57ff05f /* data mem. barrier; last f = SY */ +#define ISB WORD $0xf57ff06f /* instr. sync. barrier; last f = SY */ +#define WFI WORD $0xe320f003 /* wait for interrupt */ +#define WFI_EQ WORD $0x0320f003 /* wait for interrupt if eq */ +#define ERET WORD $0xe160006e /* exception return from HYP */ + +/* tas/cas strex debugging limits; started at 10000 */ +#define MAXSC 1000000 + +TEXT armstart(SB), 1, $-4 + + /* + * if not cpu0, go to secondary startup + */ + CPUID(R1) + BNE reset + + /* + * go to SVC mode, interrupts disabled + */ + BL svcmode(SB) + + /* + * disable the mmu and caches + */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + BIC $(CpCdcache|CpCicache|CpCmmu), R1 + ORR $(CpCsbo|CpCsw), R1 + BIC $CpCsbz, R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + BARRIERS + + /* + * 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 + + /* + * turn SMP on + * invalidate tlb + */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + ORR $CpACsmp, R1 /* turn SMP on */ + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + BARRIERS + MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv + BARRIERS + + /* + * start stack at top of mach (physical addr) + * set up page tables for kernel + */ + MOVW $PADDR(MACHADDR+MACHSIZE-4), R13 + MOVW $PADDR(L1), R0 + 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 + ORR $(CpTTBs|CpTTBowba|CpTTBiwba), R1 + MCR CpSC, 0, R1, C(CpTTB), C(0) + MCR CpSC, 0, R1, C(CpTTB), C(0), CpTTB1 /* cortex has two */ + + /* + * invalidate my caches before enabling + */ + BL cachedinv(SB) + BL cacheiinv(SB) + BL l2cacheuinv(SB) + BARRIERS + + /* + * 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 + BARRIERS + + /* + * 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<<31), R1 + MCR CpSC, 0, R1, C(CpCLD), C(CpCLDena), CpCLDenacyc + MOVW $1, R1 + MCR CpSC, 0, R1, C(CpCLD), C(CpCLDena), CpCLDenapmnc + + /* + * call main and loop forever if it returns + */ + BL ,main(SB) + B ,0(PC) + + BL _div(SB) /* hack to load _div, etc. */ + +/* + * startup entry for cpu(s) other than 0 + */ +TEXT cpureset(SB), 1, $-4 +reset: + /* + * 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 + */ + BL svcmode(SB) + + /* + * disable the mmu and caches + */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + BIC $(CpCdcache|CpCicache|CpCmmu), R1 + ORR $(CpCsbo|CpCsw), R1 + BIC $CpCsbz, R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + BARRIERS + + /* + * turn SMP on + * invalidate tlb + */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + ORR $CpACsmp, R1 /* turn SMP on */ + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + BARRIERS + MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv + BARRIERS + + /* + * find Mach for this cpu + */ + MRC CpSC, 0, R2, C(CpID), C(CpIDidct), CpIDmpid + AND $(MAXMACH-1), R2 /* mask out non-cpu-id bits */ + SLL $2, R2 /* convert to word index */ + MOVW $machaddr(SB), R0 + ADD R2, R0 /* R0 = &machaddr[cpuid] */ + MOVW (R0), R0 /* R0 = machaddr[cpuid] */ + CMP $0, R0 + BEQ 0(PC) /* must not be zero */ + SUB $KZERO, R0, R(MACH) /* m = PADDR(machaddr[cpuid]) */ + + /* + * start stack at top of local Mach + */ + ADD $(MACHSIZE-4), R(MACH), R13 + + /* + * set up domain access control and page table base + */ + MOVW $Client, R1 + MCR CpSC, 0, R1, C(CpDAC), C(0) + MOVW 12(R(MACH)), R1 /* m->mmul1 */ + SUB $KZERO, R1 /* phys addr */ + ORR $(CpTTBs|CpTTBowba|CpTTBiwba), R1 + MCR CpSC, 0, R1, C(CpTTB), C(0) + MCR CpSC, 0, R1, C(CpTTB), C(0), CpTTB1 /* cortex has two */ + + /* + * invalidate my caches before enabling + */ + BL cachedinv(SB) + BL cacheiinv(SB) + BARRIERS + + /* + * 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 + BARRIERS + + /* + * switch MACH, SB, SP, and PC into KZERO space + */ + ADD $KZERO, R(MACH) + MOVW $setR12(SB), R12 + ADD $KZERO, R13 + MOVW $_startpg2(SB), R15 + +TEXT _startpg2(SB), 1, $-4 + + /* + * enable cycle counter + */ + MOVW $(1<<31), R1 + MCR CpSC, 0, R1, C(CpCLD), C(CpCLDena), CpCLDenacyc + MOVW $1, R1 + MCR CpSC, 0, R1, C(CpCLD), C(CpCLDena), CpCLDenapmnc + + /* + * call cpustart and loop forever if it returns + */ + MRC CpSC, 0, R0, C(CpID), C(CpIDidct), CpIDmpid + AND $(MAXMACH-1), R0 /* mask out non-cpu-id bits */ + BL ,cpustart(SB) + B ,0(PC) + +/* + * get into SVC mode with interrupts disabled + * raspberry pi firmware since 29 Sept 2015 starts in HYP mode + */ +TEXT svcmode(SB), 1, $-4 + MOVW CPSR, R1 + AND $PsrMask, R1 + MOVW $PsrMhyp, R2 + CMP R2, R1 + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 + BNE nothyp + MSR(1, 1, 1, 0xe) /* MOVW R1, SPSR_HYP */ + MSR(0, 14, 1, 0xe) /* MOVW R14, ELR_HYP */ + ERET +nothyp: + MOVW R1, CPSR + RET + +TEXT cpidget(SB), 1, $-4 /* main ID */ + MRC CpSC, 0, R0, C(CpID), C(0), CpIDid + RET + +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 cpctget(SB), 1, $-4 /* cache type */ + MRC CpSC, 0, R0, C(CpID), C(CpIDidct), CpIDct + RET + +TEXT lcycles(SB), 1, $-4 + MRC CpSC, 0, R0, C(CpCLD), C(CpCLDcyc), 0 + RET + +TEXT splhi(SB), 1, $-4 + MOVW R14, 4(R(MACH)) /* save caller pc in m->splpc */ + + MOVW CPSR, R0 /* turn off irqs (but not fiqs) */ + ORR $(PsrDirq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splfhi(SB), 1, $-4 + MOVW R14, 4(R(MACH)) /* save caller pc in m->splpc */ + + 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 */ + MOVW $0, R1 + CMP.S R1, R(MACH) + MOVW.NE R1, 4(R(MACH)) /* clear m->splpc */ + BIC $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splx(SB), 1, $-4 + MOVW R14, 4(R(MACH)) /* save caller pc in m->splpc */ + + 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 /* _tas(ulong *) */ + /* returns old (R0) after modifying (R0) */ + MOVW R0,R5 + DMB + + MOVW $1,R2 /* new value of (R0) */ + MOVW $MAXSC, R8 +tas1: + LDREX(5,7) /* LDREX 0(R5),R7 */ + CMP.S $0, R7 /* old value non-zero (lock taken)? */ + BNE lockbusy /* we lose */ + SUB.S $1, R8 + BEQ lockloop2 + STREX(2,5,4) /* STREX R2,(R5),R4 */ + CMP.S $0, R4 + BNE tas1 /* strex failed? try again */ + DMB + B tas0 +lockloop2: + BL abort(SB) +lockbusy: + CLREX +tas0: + MOVW R7, R0 /* return old value */ + 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 + MOVW CPSR, R3 + ORR $(PsrDirq|PsrDfiq), R3, R1 /* splfhi */ + MOVW R1, CPSR + + DSB + MOVW nrdy(SB), R0 + CMP $0, R0 + WFI_EQ + DSB + + MOVW R3, CPSR /* splx */ + RET + + +TEXT coherence(SB), $-4 + BARRIERS + RET + +/* + * invalidate tlb + */ +TEXT mmuinvalidate(SB), 1, $-4 + DSB + 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 + DSB + MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinvse + BARRIERS + RET + +/* + * `single-element' cache operations. + * in arm arch v7, if effective to PoC, they operate on all cache levels, so separate + * l2 functions are unnecessary. + */ + +TEXT cachedwbse(SB), $-4 /* D writeback SE */ + MOVW R0, R2 + + MOVW CPSR, R3 + CPSID /* splhi */ + + BARRIERS /* force outstanding stores to cache */ + MOVW R2, R0 + MOVW 4(FP), R1 + ADD R0, R1 /* R1 is end address */ + BIC $(CACHELINESZ-1), R0 /* cache line start */ +_dwbse: + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEse + /* can't have a BARRIER here since it zeroes R0 */ + ADD $CACHELINESZ, R0 + CMP.S R0, R1 + BGT _dwbse + B _wait + +/* + * TLB on armv7 loads from cache, so no need for writeback + */ +TEXT cachedwbtlb(SB), $-4 + DSB + ISB + RET + +TEXT cachedwbinvse(SB), $-4 /* D writeback+invalidate SE */ + MOVW R0, R2 + + MOVW CPSR, R3 + CPSID /* splhi */ + + BARRIERS /* force outstanding stores to cache */ + MOVW R2, R0 + MOVW 4(FP), R1 + ADD R0, R1 /* R1 is end address */ + BIC $(CACHELINESZ-1), R0 /* cache line start */ +_dwbinvse: + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEse + /* can't have a BARRIER here since it zeroes R0 */ + ADD $CACHELINESZ, R0 + CMP.S R0, R1 + BGT _dwbinvse +_wait: /* drain write buffer */ + BARRIERS + + MOVW R3, CPSR /* splx */ + RET + +TEXT cachedinvse(SB), $-4 /* D invalidate SE */ + MOVW R0, R2 + + MOVW CPSR, R3 + CPSID /* splhi */ + + BARRIERS /* force outstanding stores to cache */ + MOVW R2, R0 + MOVW 4(FP), R1 + ADD R0, R1 /* R1 is end address */ + BIC $(CACHELINESZ-1), R0 /* cache line start */ +_dinvse: + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvd), CpCACHEse + /* can't have a BARRIER here since it zeroes R0 */ + ADD $CACHELINESZ, R0 + CMP.S R0, R1 + BGT _dinvse + B _wait + +#include "cache.v7.s" diff -Nru /n/sources/plan9/sys/src/9/bcm/cache.v7.s /sys/src/9/bcm/cache.v7.s --- /n/sources/plan9/sys/src/9/bcm/cache.v7.s Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/cache.v7.s Tue May 31 00:00:00 2016 @@ -0,0 +1,220 @@ +/* + * cortex arm arch v7 cache flushing and invalidation + * shared by l.s and rebootcode.s + */ + +#define BPIALL MCR CpSC, 0, R0, C(CpCACHE), C(5), 6 /* branch predictor invalidate all */ + +TEXT cacheiinv(SB), $-4 /* I invalidate */ + DSB + MOVW $0, R0 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall /* ok on cortex */ + BPIALL /* redundant? */ + DSB + ISB + RET + +TEXT cacheiinvse(SB), $0 /* I invalidate SE */ + MOVW 4(FP), R1 + ADD R0, R1 + BIC $(ICACHELINESZ - 1), R0 + DSB +_iinvse: + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEse + ADD $ICACHELINESZ, R0 + CMP.S R0, R1 + BGT _iinvse + BPIALL + DSB + ISB + RET + +/* + * set/way operators, passed a suitable set/way value in R0. + */ +TEXT cachedwb_sw(SB), $-4 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEsi + RET + +TEXT cachedwbinv_sw(SB), $-4 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEsi + RET + +TEXT cachedinv_sw(SB), $-4 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvd), CpCACHEsi + RET + + /* set cache size select */ +TEXT setcachelvl(SB), $-4 + MCR CpSC, CpIDcssel, R0, C(CpID), C(CpIDidct), 0 + ISB + RET + + /* return cache sizes */ +TEXT getwayssets(SB), $-4 + MRC CpSC, CpIDcsize, R0, C(CpID), C(CpIDidct), 0 + RET + +/* + * l1 cache operations. + * l1 and l2 ops are intended to be called from C, thus need save no + * caller's regs, only those we need to preserve across calls. + */ + +TEXT cachedwb(SB), $-4 + MOVW.W R14, -8(R13) + MOVW $cachedwb_sw(SB), R0 + MOVW $1, R8 + BL wholecache(SB) + MOVW.P 8(R13), R15 + +TEXT cachedwbinv(SB), $-4 + MOVW.W R14, -8(R13) + MOVW $cachedwbinv_sw(SB), R0 + MOVW $1, R8 + BL wholecache(SB) + MOVW.P 8(R13), R15 + +TEXT cachedinv(SB), $-4 + MOVW.W R14, -8(R13) + MOVW $cachedinv_sw(SB), R0 + MOVW $1, R8 + BL wholecache(SB) + MOVW.P 8(R13), R15 + +TEXT cacheuwbinv(SB), $-4 + MOVM.DB.W [R14], (R13) /* save lr on stack */ + MOVW CPSR, R1 + CPSID /* splhi */ + + MOVM.DB.W [R1], (R13) /* save R1 on stack */ + + BL cachedwbinv(SB) + BL cacheiinv(SB) + + MOVM.IA.W (R13), [R1] /* restore R1 (saved CPSR) */ + MOVW R1, CPSR + MOVM.IA.W (R13), [R14] /* restore lr */ + RET + +/* + * l2 cache operations + */ + +TEXT l2cacheuwb(SB), $-4 + MOVW.W R14, -8(R13) + MOVW $cachedwb_sw(SB), R0 + MOVW $2, R8 + BL wholecache(SB) + MOVW.P 8(R13), R15 + +TEXT l2cacheuwbinv(SB), $-4 + MOVW.W R14, -8(R13) + MOVW CPSR, R1 + CPSID /* splhi */ + + MOVM.DB.W [R1], (R13) /* save R1 on stack */ + + MOVW $cachedwbinv_sw(SB), R0 + MOVW $2, R8 + BL wholecache(SB) + BL l2cacheuinv(SB) + + MOVM.IA.W (R13), [R1] /* restore R1 (saved CPSR) */ + MOVW R1, CPSR + MOVW.P 8(R13), R15 + +TEXT l2cacheuinv(SB), $-4 + MOVW.W R14, -8(R13) + MOVW $cachedinv_sw(SB), R0 + MOVW $2, R8 + BL wholecache(SB) + MOVW.P 8(R13), R15 + +/* + * these shift values are for the Cortex-A8 L1 cache (A=2, L=6) and + * the Cortex-A8 L2 cache (A=3, L=6). + * A = log2(# of ways), L = log2(bytes per cache line). + * see armv7 arch ref p. 1403. + */ +#define L1WAYSH 30 +#define L1SETSH 6 +#define L2WAYSH 29 +#define L2SETSH 6 + +/* + * callers are assumed to be the above l1 and l2 ops. + * R0 is the function to call in the innermost loop. + * R8 is the cache level (one-origin: 1 or 2). + * + * initial translation by 5c, then massaged by hand. + */ +TEXT wholecache+0(SB), $-4 + MOVW R0, R1 /* save argument for inner loop in R1 */ + SUB $1, R8 /* convert cache level to zero origin */ + + /* we may not have the MMU on yet, so map R1 to PC's space */ + BIC $KSEGM, R1 /* strip segment from address */ + MOVW PC, R2 /* get PC's segment ... */ + AND $KSEGM, R2 + ORR R2, R1 /* combine them */ + + /* drain write buffers */ + BARRIERS + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait + ISB + + MOVW CPSR, R2 + MOVM.DB.W [R2,R14], (SP) /* save regs on stack */ + CPSID /* splhi to make entire op atomic */ + + /* get cache sizes */ + SLL $1, R8, R0 /* R0 = (cache - 1) << 1 */ + MCR CpSC, CpIDcssel, R0, C(CpID), C(CpIDidct), 0 /* set cache size select */ + ISB + MRC CpSC, CpIDcsize, R0, C(CpID), C(CpIDidct), 0 /* get cache sizes */ + + /* compute # of ways and sets for this cache level */ + SRA $3, R0, R5 /* R5 (ways) = R0 >> 3 */ + AND $1023, R5 /* R5 = (R0 >> 3) & MASK(10) */ + ADD $1, R5 /* R5 (ways) = ((R0 >> 3) & MASK(10)) + 1 */ + + SRA $13, R0, R2 /* R2 = R0 >> 13 */ + AND $32767, R2 /* R2 = (R0 >> 13) & MASK(15) */ + ADD $1, R2 /* R2 (sets) = ((R0 >> 13) & MASK(15)) + 1 */ + + /* precompute set/way shifts for inner loop */ + CMP $0, R8 /* cache == 1? */ + MOVW.EQ $L1WAYSH, R3 /* yes */ + MOVW.EQ $L1SETSH, R4 + MOVW.NE $L2WAYSH, R3 /* no */ + MOVW.NE $L2SETSH, R4 + + /* iterate over ways */ + MOVW $0, R7 /* R7: way */ +outer: + /* iterate over sets */ + MOVW $0, R6 /* R6: set */ +inner: + /* compute set/way register contents */ + SLL R3, R7, R0 /* R0 = way << R3 (L?WAYSH) */ + ORR R8<<1, R0 /* R0 = way << L?WAYSH | (cache - 1) << 1 */ + ORR R6<= sets? */ + BLT inner /* no, do next set */ + + ADD $1, R7 /* way++ */ + CMP R5, R7 /* way >= ways? */ + BLT outer /* no, do next way */ + + MOVM.IA.W (SP), [R2,R14] /* restore regs */ + MOVW R2, CPSR /* splx */ + + /* drain write buffers */ + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait + ISB + RET diff -Nru /n/sources/plan9/sys/src/9/bcm/clock.c /sys/src/9/bcm/clock.c --- /n/sources/plan9/sys/src/9/bcm/clock.c Thu Feb 21 21:22:37 2013 +++ /sys/src/9/bcm/clock.c Tue May 31 00:00:00 2016 @@ -1,11 +1,13 @@ /* - * bcm2835 timers + * bcm283[56] 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 + * Cortex-a7 has local generic timers per cpu (which we run at 1MHz) * * Use system timer 3 (64 bits) for hzclock interrupts and fastticks + * For smp on bcm2836, use local generic timer for interrupts on cpu1-3 * Use ARM timer (32 bits) for perfticks * Use ARM timer to force immediate interrupt * Use cycle counter for cycles() @@ -17,14 +19,21 @@ #include "dat.h" #include "fns.h" #include "io.h" +#include "ureg.h" +#include "arm.h" enum { SYSTIMERS = VIRTIO+0x3000, ARMTIMER = VIRTIO+0xB400, + Localctl = 0x00, + Prescaler = 0x08, + Localintpending = 0x60, + SystimerFreq = 1*Mhz, MaxPeriod = SystimerFreq / HZ, - MinPeriod = SystimerFreq / (100*HZ), + MinPeriod = 10, + }; typedef struct Systimers Systimers; @@ -64,6 +73,11 @@ TmrPrescale256 = 0x02<<2, CntWidth16 = 0<<1, CntWidth32 = 1<<1, + + /* generic timer (cortex-a7) */ + Enable = 1<<0, + Imask = 1<<1, + Istatus = 1<<2, }; static void @@ -71,12 +85,23 @@ { Systimers *tn; + if(m->machno != 0) + panic("cpu%d: unexpected system timer interrupt", m->machno); tn = (Systimers*)SYSTIMERS; /* dismiss interrupt */ tn->cs = 1<<3; timerintr(ureg, 0); } +static void +localclockintr(Ureg *ureg, void *) +{ + if(m->machno == 0) + panic("cpu0: Unexpected local generic timer interrupt"); + cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysctl, Imask|Enable); + timerintr(ureg, 0); +} + void clockshutdown(void) { @@ -84,7 +109,10 @@ tm = (Armtimer*)ARMTIMER; tm->ctl = 0; - wdogoff(); + if(cpuserver) + wdogfeed(); + else + wdogoff(); } void @@ -94,11 +122,16 @@ Armtimer *tm; u32int t0, t1, tstart, tend; - tn = (Systimers*)SYSTIMERS; - tm = (Armtimer*)ARMTIMER; - tm->load = 0; - tm->ctl = TmrPrescale1|CntEnable|CntWidth32; + if(((cprdsc(0, CpID, CpIDfeat, 1) >> 16) & 0xF) != 0) { + /* generic timer supported */ + if(m->machno == 0){ + *(ulong*)(ARMLOCAL + Localctl) = 0; /* input clock is 19.2Mhz crystal */ + *(ulong*)(ARMLOCAL + Prescaler) = 0x06aaaaab; /* divide by (2^31/Prescaler) for 1Mhz */ + } + cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysctl, Imask); + } + tn = (Systimers*)SYSTIMERS; tstart = tn->clo; do{ t0 = lcycles(); @@ -111,25 +144,36 @@ 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"); + if(m->machno == 0){ + tn->c3 = tn->clo - 1; + tm = (Armtimer*)ARMTIMER; + tm->load = 0; + tm->ctl = TmrPrescale1|CntEnable|CntWidth32; + intrenable(IRQtimer3, clockintr, nil, 0, "clock"); + }else + intrenable(IRQcntpns, localclockintr, nil, 0, "clock"); } void timerset(uvlong next) { Systimers *tn; - vlong now, period; + uvlong now; + long period; - tn = (Systimers*)SYSTIMERS; now = fastticks(nil); - period = next - fastticks(nil); + period = next - now; if(period < MinPeriod) - next = now + MinPeriod; + period = MinPeriod; else if(period > MaxPeriod) - next = now + MaxPeriod; - tn->c3 = (ulong)next; + period = MaxPeriod; + if(m->machno > 0){ + cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysval, period); + cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysctl, Enable); + }else{ + tn = (Systimers*)SYSTIMERS; + tn->c3 = tn->clo + period; + } } uvlong @@ -137,16 +181,17 @@ { Systimers *tn; ulong lo, hi; + uvlong now; - tn = (Systimers*)SYSTIMERS; if(hz) *hz = SystimerFreq; + tn = (Systimers*)SYSTIMERS; do{ hi = tn->chi; lo = tn->clo; }while(tn->chi != hi); - m->fastclock = (uvlong)hi<<32 | lo; - return m->fastclock; + now = (uvlong)hi<<32 | lo; + return now; } ulong @@ -179,7 +224,7 @@ { if(SystimerFreq != 1*Mhz) return fastticks2us(fastticks(nil)); - return fastticks(nil); + return ((Systimers*)SYSTIMERS)->clo; } void @@ -188,8 +233,8 @@ Systimers *tn; u32int now, diff; - tn = (Systimers*)SYSTIMERS; diff = n + 1; + tn = (Systimers*)SYSTIMERS; now = tn->clo; while(tn->clo - now < diff) ; diff -Nru /n/sources/plan9/sys/src/9/bcm/dat.h /sys/src/9/bcm/dat.h --- /n/sources/plan9/sys/src/9/bcm/dat.h Wed May 15 18:16:56 2013 +++ /sys/src/9/bcm/dat.h Tue May 31 00:00:00 2016 @@ -15,6 +15,7 @@ typedef struct Conf Conf; typedef struct Confmem Confmem; typedef struct FPsave FPsave; +typedef struct I2Cdev I2Cdev; typedef struct ISAConf ISAConf; typedef struct Label Label; typedef struct Lock Lock; @@ -27,6 +28,7 @@ typedef struct PMMU PMMU; typedef struct Proc Proc; typedef u32int PTE; +typedef struct Soc Soc; typedef struct Uart Uart; typedef struct Ureg Ureg; typedef uvlong Tval; @@ -119,6 +121,28 @@ int monitor; /* flag */ }; +struct I2Cdev { + int salen; + int addr; + int tenbit; +}; + +/* + * GPIO + */ +enum { + Input = 0x0, + Output = 0x1, + Alt0 = 0x4, + Alt1 = 0x5, + Alt2 = 0x6, + Alt3 = 0x7, + Alt4 = 0x3, + Alt5 = 0x2, +}; + + + /* * things saved in the Proc structure during a notify */ @@ -217,7 +241,7 @@ typedef void KMap; #define VA(k) ((uintptr)(k)) #define kmap(p) (KMap*)((p)->pa|kseg0) -#define kunmap(k) +extern void kunmap(KMap*); struct { @@ -284,3 +308,13 @@ Devport *ports; /* The ports themselves */ }; +struct Soc { /* SoC dependent configuration */ + ulong dramsize; + uintptr physio; + uintptr busdram; + uintptr busio; + uintptr armlocal; + u32int l1ptedramattrs; + u32int l2ptedramattrs; +}; +extern Soc soc; diff -Nru /n/sources/plan9/sys/src/9/bcm/devarch.c /sys/src/9/bcm/devarch.c --- /n/sources/plan9/sys/src/9/bcm/devarch.c Sat Jul 28 09:36:57 2012 +++ /sys/src/9/bcm/devarch.c Tue May 31 00:00:00 2016 @@ -150,9 +150,19 @@ static long cputyperead(Chan*, void *a, long n, vlong offset) { - char str[128]; + char name[64], str[128]; - snprint(str, sizeof str, "ARM11 %d\n", m->cpumhz); + cputype2name(name, sizeof name); + snprint(str, sizeof str, "ARM %s %d\n", name, m->cpumhz); + return readstr(offset, a, n, str); +} + +static long +cputempread(Chan*, void *a, long n, vlong offset) +{ + char str[16]; + + snprint(str, sizeof str, "%ud\n", (getcputemp()+500)/1000); return readstr(offset, a, n, str); } @@ -160,4 +170,5 @@ archinit(void) { addarchfile("cputype", 0444, cputyperead, nil); + addarchfile("cputemp", 0444, cputempread, nil); } diff -Nru /n/sources/plan9/sys/src/9/bcm/devgpio.c /sys/src/9/bcm/devgpio.c --- /n/sources/plan9/sys/src/9/bcm/devgpio.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/devgpio.c Tue May 31 00:00:00 2016 @@ -0,0 +1,161 @@ +/* + * Raspberry Pi (BCM2835) GPIO + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum { + // GPIO registers + GPLEV = 0x7e200034, +}; + +enum{ + Qdir = 0, + Qgpio, +}; + +Dirtab gpiodir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "gpio", {Qgpio, 0}, 0, 0664, +}; + +enum { + // commands + CMfunc, + CMset, + CMpullup, + CMpulldown, + CMfloat, +}; + +static Cmdtab gpiocmd[] = { + {CMfunc, "function", 3}, + {CMset, "set", 3}, + {CMpullup, "pullup", 2}, + {CMpulldown, "pulldown", 2}, + {CMfloat, "float", 2}, +}; + +static char *funcs[] = { "in", "out", "alt5", "alt4", "alt0", + "alt1", "alt2", "alt3", "pulse"}; +static int ifuncs[] = { Input, Output, Alt5, Alt4, Alt0, + Alt1, Alt2, Alt3, -1}; + +static Chan* +gpioattach(char* spec) +{ + return devattach('G', spec); +} + +static Walkqid* +gpiowalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, gpiodir, nelem(gpiodir), devgen); +} + +static int +gpiostat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, gpiodir, nelem(gpiodir), devgen); +} + +static Chan* +gpioopen(Chan* c, int omode) +{ + return devopen(c, omode, gpiodir, nelem(gpiodir), devgen); +} + +static void +gpioclose(Chan*) +{ +} + +static long +gpioread(Chan* c, void *buf, long n, vlong) +{ + char lbuf[20]; + char *e; + + USED(c); + if(c->qid.path == Qdir) + return devdirread(c, buf, n, gpiodir, nelem(gpiodir), devgen); + e = lbuf + sizeof(lbuf); + seprint(lbuf, e, "%08ulx%08ulx", ((ulong *)GPLEV)[1], ((ulong *)GPLEV)[0]); + return readstr(0, buf, n, lbuf); +} + +static long +gpiowrite(Chan* c, void *buf, long n, vlong) +{ + Cmdbuf *cb; + Cmdtab *ct; + int pin, i; + + if(c->qid.type & QTDIR) + error(Eperm); + cb = parsecmd(buf, n); + if(waserror()) { + free(cb); + nexterror(); + } + ct = lookupcmd(cb, gpiocmd, nelem(gpiocmd)); + pin = atoi(cb->f[1]); + switch(ct->index) { + case CMfunc: + for(i = 0; i < nelem(funcs); i++) + if(strcmp(funcs[i], cb->f[2]) == 0) + break; + if(i >= nelem(funcs)) + error(Ebadctl); + if(ifuncs[i] == -1) { + gpiosel(pin, Output); + microdelay(2); + gpiosel(pin, Input); + } + else { + gpiosel(pin, ifuncs[i]); + } + break; + case CMset: + gpioout(pin, atoi(cb->f[2])); + break; + case CMpullup: + gpiopullup(pin); + break; + case CMpulldown: + gpiopulldown(pin); + break; + case CMfloat: + gpiopulloff(pin); + break; + } + free(cb); + poperror(); + return n; +} + +Dev gpiodevtab = { + 'G', + "gpio", + + devreset, + devinit, + devshutdown, + gpioattach, + gpiowalk, + gpiostat, + gpioopen, + devcreate, + gpioclose, + gpioread, + devbread, + gpiowrite, + devbwrite, + devremove, + devwstat, +}; diff -Nru /n/sources/plan9/sys/src/9/bcm/devi2c.c /sys/src/9/bcm/devi2c.c --- /n/sources/plan9/sys/src/9/bcm/devi2c.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/devi2c.c Tue May 31 00:00:00 2016 @@ -0,0 +1,227 @@ +/* + * i2c + * + * Copyright © 1998, 2003 Vita Nuova Limited. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +typedef struct I2Cdir I2Cdir; + +enum{ + Qdir, + Qdata, + Qctl, +}; + +static +Dirtab i2ctab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "i2cdata", {Qdata, 0}, 256, 0660, + "i2cctl", {Qctl, 0}, 0, 0660, +}; + +struct I2Cdir { + Ref; + I2Cdev; + Dirtab tab[nelem(i2ctab)]; +}; + +static void +i2creset(void) +{ + i2csetup(0); +} + +static Chan* +i2cattach(char* spec) +{ + char *s; + ulong addr; + I2Cdir *d; + Chan *c; + + addr = strtoul(spec, &s, 16); + if(*spec == 0 || *s || addr >= (1<<10)) + error("invalid i2c address"); + d = malloc(sizeof(I2Cdir)); + if(d == nil) + error(Enomem); + d->ref = 1; + d->addr = addr; + d->salen = 0; + d->tenbit = addr >= 128; + memmove(d->tab, i2ctab, sizeof(d->tab)); + sprint(d->tab[1].name, "i2c.%lux.data", addr); + sprint(d->tab[2].name, "i2c.%lux.ctl", addr); + + c = devattach('J', spec); + c->aux = d; + return c; +} + +static Walkqid* +i2cwalk(Chan* c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + I2Cdir *d; + + d = c->aux; + wq = devwalk(c, nc, name, nname, d->tab, nelem(d->tab), devgen); + if(wq != nil && wq->clone != nil && wq->clone != c) + incref(d); + return wq; +} + +static int +i2cstat(Chan* c, uchar *dp, int n) +{ + I2Cdir *d; + + d = c->aux; + return devstat(c, dp, n, d->tab, nelem(d->tab), devgen); +} + +static Chan* +i2copen(Chan* c, int omode) +{ + I2Cdir *d; + + d = c->aux; + return devopen(c, omode, d->tab, nelem(d->tab), devgen); +} + +static void +i2cclose(Chan *c) +{ + I2Cdir *d; + + d = c->aux; + if(decref(d) == 0) + free(d); +} + +static long +i2cread(Chan *c, void *a, long n, vlong offset) +{ + I2Cdir *d; + char *s, *e; + ulong len; + + d = c->aux; + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, d->tab, nelem(d->tab), devgen); + case Qdata: + len = d->tab[1].length; + if(offset+n >= len){ + n = len - offset; + if(n <= 0) + return 0; + } + n = i2crecv(d, a, n, offset); + break; + case Qctl: + s = smalloc(READSTR); + if(waserror()){ + free(s); + nexterror(); + } + e = seprint(s, s+READSTR, "size %lud\n", (ulong)d->tab[1].length); + if(d->salen) + e = seprint(e, s+READSTR, "subaddress %d\n", d->salen); + if(d->tenbit) + seprint(e, s+READSTR, "a10\n"); + n = readstr(offset, a, n, s); + poperror(); + free(s); + return n; + default: + n=0; + break; + } + return n; +} + +static long +i2cwrite(Chan *c, void *a, long n, vlong offset) +{ + I2Cdir *d; + long len; + Cmdbuf *cb; + + USED(offset); + switch((ulong)c->qid.path){ + case Qdata: + d = c->aux; + len = d->tab[1].length; + if(offset+n >= len){ + n = len - offset; + if(n <= 0) + return 0; + } + n = i2csend(d, a, n, offset); + break; + case Qctl: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf < 1) + error(Ebadctl); + d = c->aux; + if(strcmp(cb->f[0], "subaddress") == 0){ + if(cb->nf > 1){ + len = strtol(cb->f[1], nil, 0); + if(len <= 0) + len = 0; + if(len > 4) + cmderror(cb, "subaddress too long"); + }else + len = 1; + d->salen = len; + }else if(cb->nf > 1 && strcmp(cb->f[0], "size") == 0){ + len = strtol(cb->f[1], nil, 0); + if(len < 0) + cmderror(cb, "size is negative"); + d->tab[1].length = len; + }else if(strcmp(cb->f[0], "a10") == 0) + d->tenbit = 1; + else + cmderror(cb, "unknown control request"); + poperror(); + free(cb); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev i2cdevtab = { + 'J', + "i2c", + + i2creset, + devinit, + devshutdown, + i2cattach, + i2cwalk, + i2cstat, + i2copen, + devcreate, + i2cclose, + i2cread, + devbread, + i2cwrite, + devbwrite, + devremove, + devwstat, +}; diff -Nru /n/sources/plan9/sys/src/9/bcm/devrtc3231.c /sys/src/9/bcm/devrtc3231.c --- /n/sources/plan9/sys/src/9/bcm/devrtc3231.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/devrtc3231.c Tue May 31 00:00:00 2016 @@ -0,0 +1,371 @@ +/* + * Maxim DS3231 realtime clock (accessed via rtc) + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum { + /* DS3231 registers */ + Seconds= 0, + Minutes= 1, + Hours= 2, + Weekday= 3, + Mday= 4, + Month= 5, + Year= 6, + Nbcd= 7, + + /* Hours register may be in 12-hour or 24-hour mode */ + Twelvehr= 1<<6, + Pm= 1<<5, + + I2Caddr= 0x68, + +}; + +typedef struct Rtc Rtc; + +struct Rtc +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; +}; + +enum{ + Qdir = 0, + Qrtc, +}; + +Dirtab rtcdir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "rtc", {Qrtc, 0}, 0, 0664, +}; + +static ulong rtc2sec(Rtc*); +static void sec2rtc(ulong, Rtc*); + +static void +i2cread(uint addr, void *buf, int len) +{ + I2Cdev d; + + d.addr = addr; + d.tenbit = 0; + d.salen = 0; + i2crecv(&d, buf, len, 0); +} + +static void +i2cwrite(uint addr, void *buf, int len) +{ + I2Cdev d; + + d.addr = addr; + d.tenbit = 0; + d.salen = 0; + i2csend(&d, buf, len, 0); +} + +static void +rtcinit() +{ + i2csetup(0); +} + +static Chan* +rtcattach(char* spec) +{ + return devattach('r', spec); +} + +static Walkqid* +rtcwalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen); +} + +static int +rtcstat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen); +} + +static Chan* +rtcopen(Chan* c, int omode) +{ + char dummy; + + omode = openmode(omode); + switch((ulong)c->qid.path){ + case Qrtc: + if(strcmp(up->user, eve)!=0 && omode!=OREAD) + error(Eperm); + /* if it's not there, this will throw an error */ + i2cread(I2Caddr, &dummy, 1); + break; + } + return devopen(c, omode, rtcdir, nelem(rtcdir), devgen); +} + +static void +rtcclose(Chan*) +{ +} + +static int +bcd(int n) +{ + return (n & 0xF) + (10 * (n >> 4)); +} + +long +rtctime(void) +{ + uchar clk[Nbcd]; + Rtc rtc; + + clk[0] = 0; + i2cwrite(I2Caddr, clk, 1); + i2cread(I2Caddr, clk, Nbcd); + + /* + * convert from BCD + */ + rtc.sec = bcd(clk[Seconds]); + rtc.min = bcd(clk[Minutes]); + rtc.hour = bcd(clk[Hours]); + if(clk[Hours] & Twelvehr){ + rtc.hour = bcd(clk[Hours] & 0x1F); + if(clk[Hours] & Pm) + rtc.hour += 12; + } + rtc.mday = bcd(clk[Mday]); + rtc.mon = bcd(clk[Month] & 0x1F); + rtc.year = bcd(clk[Year]); + + /* + * the world starts jan 1 1970 + */ + if(rtc.year < 70) + rtc.year += 2000; + else + rtc.year += 1900; + return rtc2sec(&rtc); +} + + +static long +rtcread(Chan* c, void* buf, long n, vlong off) +{ + ulong t; + ulong offset = off; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + t = rtctime(); + n = readnum(offset, buf, n, t, 12); + return n; + } + error(Ebadarg); + return 0; +} + +#define PUTBCD(n,o) bcdclock[1+o] = (n % 10) | (((n / 10) % 10)<<4) + +static long +rtcwrite(Chan* c, void* buf, long n, vlong off) +{ + Rtc rtc; + ulong secs; + uchar bcdclock[1+Nbcd]; + char *cp, *ep; + ulong offset = off; + + if(offset!=0) + error(Ebadarg); + + + switch((ulong)c->qid.path){ + case Qrtc: + /* + * read the time + */ + cp = ep = buf; + ep += n; + while(cp < ep){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + + /* + * convert to bcd + */ + sec2rtc(secs, &rtc); + PUTBCD(rtc.sec, Seconds); + PUTBCD(rtc.min, Minutes); /* forces 24 hour mode */ + PUTBCD(rtc.hour, Hours); + PUTBCD(0, Weekday); /* hope no other OS uses this */ + PUTBCD(rtc.mday, Mday); + PUTBCD(rtc.mon, Month); + PUTBCD(rtc.year, Year); + + /* + * write the clock + */ + bcdclock[0] = 0; + i2cwrite(I2Caddr, bcdclock, 1+Nbcd); + return n; + } + error(Ebadarg); + return 0; +} + +Dev rtc3231devtab = { + 'r', + "rtc", + + devreset, + rtcinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + devcreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, +}; + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +static int dmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +static int ldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +static int* +yrsize(int y) +{ + if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) + return ldmsize; + else + return dmsize; +} + +/* + * compute seconds since Jan 1 1970 + */ +static ulong +rtc2sec(Rtc *rtc) +{ + ulong secs; + int i; + int *d2m; + + secs = 0; + + /* + * seconds per year + */ + for(i = 1970; i < rtc->year; i++){ + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * seconds per month + */ + d2m = yrsize(rtc->year); + for(i = 1; i < rtc->mon; i++) + secs += d2m[i] * SEC2DAY; + + secs += (rtc->mday-1) * SEC2DAY; + secs += rtc->hour * SEC2HOUR; + secs += rtc->min * SEC2MIN; + secs += rtc->sec; + + return secs; +} + +/* + * compute rtc from seconds since Jan 1 1970 + */ +static void +sec2rtc(ulong secs, Rtc *rtc) +{ + int d; + long hms, day; + int *d2m; + + /* + * break initial number into days + */ + hms = secs % SEC2DAY; + day = secs / SEC2DAY; + if(hms < 0) { + hms += SEC2DAY; + day -= 1; + } + + /* + * generate hours:minutes:seconds + */ + rtc->sec = hms % 60; + d = hms / 60; + rtc->min = d % 60; + d /= 60; + rtc->hour = d; + + /* + * year number + */ + if(day >= 0) + for(d = 1970; day >= *yrsize(d); d++) + day -= *yrsize(d); + else + for (d = 1970; day < 0; d--) + day += *yrsize(d-1); + rtc->year = d; + + /* + * generate month + */ + d2m = yrsize(rtc->year); + for(d = 1; day >= d2m[d]; d++) + day -= d2m[d]; + rtc->mday = day + 1; + rtc->mon = d; + + return; +} diff -Nru /n/sources/plan9/sys/src/9/bcm/devspi.c /sys/src/9/bcm/devspi.c --- /n/sources/plan9/sys/src/9/bcm/devspi.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/devspi.c Tue May 31 00:00:00 2016 @@ -0,0 +1,231 @@ +/* + * minimal spi interface for testing + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +extern int qstate(Queue*); + +enum { + QMAX = 64*1024, + Nspislave = 2, +}; + +typedef struct Spi Spi; + +struct Spi { + int csel; + int opens; + QLock; + Queue *iq; + Queue *oq; +}; + +Spi spidev[Nspislave]; + +enum{ + Qdir = 0, + Qctl, + Qspi, +}; + +Dirtab spidir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "spictl", {Qctl, 0}, 0, 0664, + "spi0", {Qspi+0, 0}, 0, 0664, + "spi1", {Qspi+1, 0}, 0, 0664, +}; + +#define DEVID(path) ((ulong)path - Qspi) + +enum { + CMclock, + CMmode, + CMlossi, +}; + +Cmdtab spitab[] = { + {CMclock, "clock", 2}, + {CMmode, "mode", 2}, + {CMlossi, "lossi", 1}, +}; + +static void +spikick(void *a) +{ + Block *b; + Spi *spi; + + spi = a; + b = qget(spi->oq); + if(b == nil) + return; + if(waserror()){ + freeb(b); + nexterror(); + } + spirw(spi->csel, b->rp, BLEN(b)); + qpass(spi->iq, b); + poperror(); +} + +static void +spiinit(void) +{ +} + +static long +spiread(Chan *c, void *a, long n, vlong off) +{ + Spi *spi; + u32int *sp; + char *p, *e; + char buf[256]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, spidir, nelem(spidir), devgen); + + if(c->qid.path == Qctl) { + sp = (u32int *)0x7e204000; + p = buf; + e = p + sizeof(buf); + p = seprint(p, e, "CS: %08x\n", sp[0]); + p = seprint(p, e, "CLK: %08x\n", sp[2]); + p = seprint(p, e, "DLEN: %08x\n", sp[3]); + p = seprint(p, e, "LTOH: %08x\n", sp[4]); + seprint(p, e, "DC: %08x\n", sp[5]); + return readstr(off, a, n, buf); + } + + spi = &spidev[DEVID(c->qid.path)]; + n = qread(spi->iq, a, n); + + return n; +} + +static long +spiwrite(Chan*c, void *a, long n, vlong) +{ + Spi *spi; + Cmdbuf *cb; + Cmdtab *ct; + + if(c->qid.type & QTDIR) + error(Eperm); + + if(c->qid.path == Qctl) { + cb = parsecmd(a, n); + if(waserror()) { + free(cb); + nexterror(); + } + ct = lookupcmd(cb, spitab, nelem(spitab)); + switch(ct->index) { + case CMclock: + spiclock(atoi(cb->f[1])); + break; + case CMmode: + spimode(atoi(cb->f[1])); + break; + case CMlossi: + break; + } + poperror(); + return n; + } + + spi = &spidev[DEVID(c->qid.path)]; + n = qwrite(spi->oq, a, n); + + return n; +} + +static Chan* +spiattach(char* spec) +{ + return devattach(L'π', spec); +} + +static Walkqid* +spiwalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, spidir, nelem(spidir), devgen); +} + +static int +spistat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, spidir, nelem(spidir), devgen); +} + +static Chan* +spiopen(Chan* c, int omode) +{ + Spi *spi; + + c = devopen(c, omode, spidir, nelem(spidir), devgen); + if(c->qid.type & QTDIR) + return c; + + spi = &spidev[DEVID(c->qid.path)]; + qlock(spi); + if(spi->opens++ == 0){ + spi->csel = DEVID(c->qid.path); + if(spi->iq == nil) + spi->iq = qopen(QMAX, 0, nil, nil); + else + qreopen(spi->iq); + if(spi->oq == nil) + spi->oq = qopen(QMAX, Qkick, spikick, spi); + else + qreopen(spi->oq); + } + qunlock(spi); + c->iounit = qiomaxatomic; + return c; +} + +static void +spiclose(Chan *c) +{ + Spi *spi; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + spi = &spidev[DEVID(c->qid.path)]; + qlock(spi); + if(--spi->opens == 0){ + qclose(spi->iq); + qhangup(spi->oq, nil); + qclose(spi->oq); + } + qunlock(spi); +} + +Dev spidevtab = { + L'π', + "spi", + + devreset, + spiinit, + devshutdown, + spiattach, + spiwalk, + spistat, + spiopen, + devcreate, + spiclose, + spiread, + devbread, + spiwrite, + devbwrite, + devremove, + devwstat, +}; + diff -Nru /n/sources/plan9/sys/src/9/bcm/dma.c /sys/src/9/bcm/dma.c --- /n/sources/plan9/sys/src/9/bcm/dma.c Tue Jan 29 22:07:45 2013 +++ /sys/src/9/bcm/dma.c Tue May 31 00:00:00 2016 @@ -25,7 +25,7 @@ enum { Nchan = 7, /* number of dma channels */ Regsize = 0x100, /* size of regs for each chan */ - Cbalign = 32, /* control block byte alignment */ + Cbalign = 64, /* control block byte alignment (allow for 64-byte cache on bcm2836) */ Dbg = 0, /* registers for each dma controller */ @@ -97,6 +97,18 @@ static Ctlr dma[Nchan]; static u32int *dmaregs = (u32int*)DMAREGS; +uintptr +dmaaddr(void *va) +{ + return soc.busdram | (PTR2UINT(va) & ~KSEGM); +} + +static uintptr +dmaioaddr(void *va) +{ + return soc.busio | (PTR2UINT(va) & ~VIRTIO); +} + static void dump(char *msg, uchar *p, int n) { @@ -156,23 +168,23 @@ ti = 0; switch(dir){ case DmaD2M: - cachedwbinvse(dst, len); + cachedinvse(dst, len); ti = Srcdreq | Destinc; - cb->sourcead = DMAIO(src); - cb->destad = DMAADDR(dst); + cb->sourcead = dmaioaddr(src); + cb->destad = dmaaddr(dst); break; case DmaM2D: cachedwbse(src, len); ti = Destdreq | Srcinc; - cb->sourcead = DMAADDR(src); - cb->destad = DMAIO(dst); + cb->sourcead = dmaaddr(src); + cb->destad = dmaioaddr(dst); break; case DmaM2M: cachedwbse(src, len); - cachedwbinvse(dst, len); + cachedinvse(dst, len); ti = Srcinc | Destinc; - cb->sourcead = DMAADDR(src); - cb->destad = DMAADDR(dst); + cb->sourcead = dmaaddr(src); + cb->destad = dmaaddr(dst); break; } cb->ti = ti | dev<regs[Cs] = 0; microdelay(1); - ctlr->regs[Conblkad] = DMAADDR(cb); + 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); diff -Nru /n/sources/plan9/sys/src/9/bcm/emmc.c /sys/src/9/bcm/emmc.c --- /n/sources/plan9/sys/src/9/bcm/emmc.c Tue Jan 29 22:07:37 2013 +++ /sys/src/9/bcm/emmc.c Tue May 31 00:00:00 2016 @@ -178,7 +178,11 @@ static int datadone(void*) { - return emmc.datadone; + int i; + + u32int *r = (u32int*)EMMCREGS; + i = r[Interrupt]; + return i & (Datadone|Err); } static int @@ -310,9 +314,9 @@ if((c & Respmask) == Resp48busy){ WR(Irpten, Datadone|Err); tsleep(&emmc.r, datadone, 0, 3000); - i = emmc.datadone; - emmc.datadone = 0; WR(Irpten, 0); + emmc.datadone = 0; + i = r[Interrupt]; if((i & Datadone) == 0) print("emmcio: no Datadone after CMD%d\n", cmd); if(i & Err) @@ -380,11 +384,13 @@ &r[Data], buf, len); if(dmawait(DmaChanEmmc) < 0) error(Eio); + if(!write) + cachedinvse(buf, len); WR(Irpten, Datadone|Err); tsleep(&emmc.r, datadone, 0, 3000); - i = emmc.datadone; - emmc.datadone = 0; WR(Irpten, 0); + emmc.datadone = 0; + i = r[Interrupt]; if((i & Datadone) == 0){ print("emmcio: %d timeout intr %ux stat %ux\n", write, i, r[Status]); @@ -407,13 +413,11 @@ mmcinterrupt(Ureg*, void*) { u32int *r; - int i; - r = (u32int*)EMMCREGS; - i = r[Interrupt]; - r[Interrupt] = i & (Datadone|Err); - emmc.datadone = i; - wakeup(&emmc.r); + if(r[Interrupt]&(Datadone|Err)){ + WR(Irpten, 0); + wakeup(&emmc.r); + } } SDio sdio = { diff -Nru /n/sources/plan9/sys/src/9/bcm/etherusb.c /sys/src/9/bcm/etherusb.c --- /n/sources/plan9/sys/src/9/bcm/etherusb.c Tue Sep 17 23:37:40 2013 +++ /sys/src/9/bcm/etherusb.c Tue May 31 00:00:00 2016 @@ -198,10 +198,10 @@ n = BLEN(b) & 0xFFFF; n |= ~n << 16; - padblock(b, 4); + b = padblock(b, 4); PUT4(b->rp, n); if(BLEN(b) % ctlr->maxpkt == 0){ - padblock(b, -4); + b = padblock(b, -4); PUT4(b->wp, 0xFFFF0000); b->wp += 4; } @@ -214,7 +214,7 @@ int n; n = BLEN(b) & 0x7FF; - padblock(b, 8); + b = padblock(b, 8); PUT4(b->rp, n | SmscTxfirst | SmscTxlast); PUT4(b->rp+4, n); transmit(ctlr, b); @@ -263,6 +263,8 @@ Chan *inchan, *outchan; char *buf; uint bufsize, maxpkt; + uchar ea[Eaddrlen]; + static char nullea[Eaddrlen]; qlock(ctlr); inchan = outchan = nil; @@ -288,9 +290,13 @@ inchan = namec(cb->f[2], Aopen, OREAD, 0); outchan = namec(cb->f[3], Aopen, OWRITE, 0); assert(inchan != nil && outchan != nil); - if(parsemac(ctlr->edev->ea, cb->f[4], Eaddrlen) != Eaddrlen) + if(parsemac(ea, cb->f[4], Eaddrlen) != Eaddrlen) cmderror(cb, "bad etheraddr"); - memmove(ctlr->edev->addr, ctlr->edev->ea, Eaddrlen); + if(memcmp(ctlr->edev->ea, nullea, Eaddrlen) == 0) + memmove(ctlr->edev->ea, ea, Eaddrlen); + else if(memcmp(ctlr->edev->ea, ea, Eaddrlen) != 0) + cmderror(cb, "wrong ether address"); + memmove(ctlr->edev->addr, ea, Eaddrlen); print("\netherusb %s: %E\n", udev->name, ctlr->edev->addr); ctlr->buf = buf; ctlr->inchan = inchan; @@ -397,6 +403,17 @@ } static void +etherusbmulticast(void*, uchar*, int) +{ + /* nothing to do, we allow all multicast packets in */ +} + +static void +etherusbshutdown(Ether*) +{ +} + +static void etherusbattach(Ether* edev) { Ctlr *ctlr; @@ -427,8 +444,8 @@ edev->arg = edev; /* TODO: promiscuous, multicast (for ipv6), shutdown (for reboot) */ // edev->promiscuous = etherusbpromiscuous; -// edev->shutdown = etherusbshutdown; -// edev->multicast = etherusbmulticast; + edev->shutdown = etherusbshutdown; + edev->multicast = etherusbmulticast; return 0; } diff -Nru /n/sources/plan9/sys/src/9/bcm/fns.h /sys/src/9/bcm/fns.h --- /n/sources/plan9/sys/src/9/bcm/fns.h Thu May 15 03:01:41 2014 +++ /sys/src/9/bcm/fns.h Tue May 31 00:00:00 2016 @@ -5,24 +5,32 @@ extern void archreboot(void); extern void archreset(void); extern void armtimerset(int); +extern void cachedwb(void); extern void cachedwbinv(void); +extern void cachedinvse(void*, int); extern void cachedwbse(void*, int); extern void cachedwbinvse(void*, int); +extern void cachedwbtlb(void*, int); extern void cacheiinv(void); +extern void cacheiinvse(void*, int); extern void cacheuwbinv(void); extern uintptr cankaddr(uintptr pa); extern int cas32(void*, u32int, u32int); +extern int cas(ulong*, ulong, ulong); extern void checkmmu(uintptr, uintptr); extern void clockinit(void); extern void clockshutdown(void); extern int cmpswap(long*, long, long); extern void coherence(void); +extern u32int cpidget(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 char *cputype2name(char *buf, int size); 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 uintptr dmaaddr(void *va); extern void dmastart(int, int, int, void*, void*, int); extern int dmawait(int); extern int fbblank(int); @@ -35,19 +43,35 @@ extern ulong fpsavereg(int fpreg, uvlong *fpp); extern void fpwr(int fpreg, ulong val); extern u32int fsrget(void); +extern uint getboardrev(void); extern ulong getclkrate(int); extern char* getconf(char*); +extern uint getcputemp(void); extern char *getethermac(void); extern uint getfirmware(void); +extern int getncpus(void); extern int getpower(int); extern void getramsize(Confmem*); +extern void gpiosel(uint, int); +extern void gpiopullup(uint); +extern void gpiopulloff(uint); +extern void gpiopulldown(uint); +extern void gpioout(uint, int); +extern int gpioin(unit); +extern void i2csetup(int); +extern long i2crecv(I2Cdev*, void*, long, ulong); +extern long i2csend(I2Cdev*, void*, long, ulong); 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 intrcpushutdown(void); +extern void intrshutdown(void); extern void intrsoff(void); extern int isaconfig(char*, int, ISAConf*); +extern int l2ap(int); +extern void l2cacheuwbinv(void); extern void links(void); -extern void mmuinit(void); +extern void mmuinit(void*); extern void mmuinit1(void); extern void mmuinvalidate(void); extern void mmuinvalidateaddr(u32int); @@ -58,22 +82,31 @@ extern void procsetup(Proc*); extern void screeninit(void); #define sdfree(p) free(p) -#define sdmalloc(n) mallocalign(n, CACHELINESZ, 0, 0) +#define sdmalloc(n) mallocalign(n, BLOCKALIGN, 0, 0) +extern void setclkrate(int, ulong); extern void setpower(int, int); extern void setr13(int, u32int*); +extern void spiclock(uint); +extern void spimode(int); +extern void spirw(uint, void*, int); extern int splfhi(void); extern int splflo(void); extern void swcursorinit(void); extern void syscallfmt(int syscallno, ulong pc, va_list list); extern void sysretfmt(int syscallno, va_list list, long ret, uvlong start, uvlong stop); +extern int startcpus(uint); +extern void stopcpu(uint); 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 vgpinit(void); +extern void vgpset(uint, int); extern void vtable(void); extern void wdogoff(void); +extern void wdogfeed(void); /* * floating point emulation @@ -90,6 +123,18 @@ extern void fpusysrforkchild(Proc*, Ureg*, Proc*); extern int fpuemu(Ureg*); /* + * Miscellaneous machine dependent stuff. + */ +extern char* getenv(char*, char*, int); +uintptr mmukmap(uintptr, uintptr, usize); +uintptr mmukunmap(uintptr, uintptr, usize); +extern void* mmuuncache(void*, usize); +extern void* ucalloc(usize); +extern Block* ucallocb(int); +extern void* ucallocalign(usize size, int align, int span); +extern void ucfree(void*); +extern void ucfreeb(Block*); +/* * Things called from port. */ extern void delay(int); /* only scheddump() */ @@ -114,7 +159,5 @@ #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 -Nru /n/sources/plan9/sys/src/9/bcm/fpiarm.c /sys/src/9/bcm/fpiarm.c --- /n/sources/plan9/sys/src/9/bcm/fpiarm.c Mon Mar 25 20:29:08 2013 +++ /sys/src/9/bcm/fpiarm.c Tue May 31 00:00:00 2016 @@ -220,6 +220,7 @@ { void *mem; + validaddr(ea, n, 0); mem = (void*)ea; (*f)(&FR(ufp, d), mem); if(fpemudebug) @@ -232,6 +233,7 @@ Internal tmp; void *mem; + validaddr(ea, n, 1); mem = (void*)ea; tmp = FR(ufp, s); if(fpemudebug) diff -Nru /n/sources/plan9/sys/src/9/bcm/gpio.c /sys/src/9/bcm/gpio.c --- /n/sources/plan9/sys/src/9/bcm/gpio.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/gpio.c Tue May 31 00:00:00 2016 @@ -0,0 +1,95 @@ +/* + * Raspberry Pi GPIO support + */ + +#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) + +/* GPIO regs */ +enum { + Fsel0 = 0x00>>2, + FuncMask= 0x7, + Set0 = 0x1c>>2, + Clr0 = 0x28>>2, + Lev0 = 0x34>>2, + PUD = 0x94>>2, + Off = 0x0, + Pulldown= 0x1, + Pullup = 0x2, + PUDclk0 = 0x98>>2, + PUDclk1 = 0x9c>>2, +}; + +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< 100 KHz assuming 150Mhz input clock */ + u32int delay; /* default (48<<16)|48 falling:rising edge */ + u32int clktimeout; /* default 64 */ +}; + +/* + * Per-controller info + */ +struct I2c { + QLock lock; + Lock reglock; + Rendez r; + Bsc *regs; +}; + +static I2c i2c; + +enum { + /* ctrl */ + I2cen = 1<<15, /* I2c enable */ + Intr = 1<<10, /* interrupt on reception */ + Intt = 1<<9, /* interrupt on transmission */ + Intd = 1<<8, /* interrupt on done */ + Start = 1<<7, /* aka ST, start a transfer */ + Clear = 1<<4, /* clear fifo */ + Read = 1<<0, /* read transfer */ + Write = 0<<0, /* write transfer */ + + /* stat */ + Clkt = 1<<9, /* clock stretch timeout */ + Err = 1<<8, /* NAK */ + Rxf = 1<<7, /* RX fifo full */ + Txe = 1<<6, /* TX fifo full */ + Rxd = 1<<5, /* RX fifo has data */ + Txd = 1<<4, /* TX fifo has space */ + Rxr = 1<<3, /* RX fiio needs reading */ + Txw = 1<<2, /* TX fifo needs writing */ + Done = 1<<1, /* transfer done */ + Ta = 1<<0, /* Transfer active */ + + /* maximum I2C I/O (can change) */ + MaxIO = 128, + MaxSA = 2, /* longest subaddress */ + Bufsize = (MaxIO+MaxSA+1+4)&~3, /* extra space for subaddress/clock bytes and alignment */ + + Chatty = 0, +}; + +static void +i2cinterrupt(Ureg*, void*) +{ + Bsc *r; + int st; + + r = i2c.regs; + st = 0; + if((r->ctrl & Intr) && (r->stat & Rxd)) + st |= Intr; + if((r->ctrl & Intt) && (r->stat & Txd)) + st |= Intt; + if(r->stat & Done) + st |= Intd; + if(st){ + r->ctrl &= ~st; + wakeup(&i2c.r); + } +} + +static int +i2cready(void *st) +{ + return (i2c.regs->stat & (uintptr)st); +} + +static void +i2cinit(void) +{ + i2c.regs = (Bsc*)I2CREGS; + i2c.regs->clkdiv = 2500; + + gpiosel(SDA0Pin, Alt0); + gpiosel(SCL0Pin, Alt0); + gpiopullup(SDA0Pin); + gpiopullup(SCL0Pin); + + intrenable(IRQi2c, i2cinterrupt, 0, 0, "i2c"); +} + +/* + * To do subaddressing avoiding a STOP condition between the address and payload. + * - write the subaddress, + * - poll until the transfer starts, + * - overwrite the registers for the payload transfer, before the subaddress + * transaction has completed. + * + * FIXME: neither 10bit adressing nor 100Khz transfers implemented yet. + */ +static void +i2cio(int rw, int tenbit, uint addr, void *buf, int len, int salen, uint subaddr) +{ + Bsc *r; + uchar *p; + int st; + + if(tenbit) + error("10bit addressing not supported"); + if(salen == 0 && subaddr) /* default subaddress len == 1byte */ + salen = 1; + + qlock(&i2c.lock); + r = i2c.regs; + r->ctrl = I2cen | Clear; + r->addr = addr; + r->stat = Clkt|Err|Done; + + if(salen){ + r->dlen = salen; + r->ctrl = I2cen | Start | Write; + while((r->stat & Ta) == 0) { + if(r->stat & (Err|Clkt)) { + qunlock(&i2c.lock); + error(Eio); + } + } + r->dlen = len; + r->ctrl = I2cen | Start | Intd | rw; + for(; salen > 0; salen--) + r->fifo = subaddr >> ((salen-1)*8); + /* + * Adapted from Linux code...uses undocumented + * status information. + */ + if(rw == Read) { + do { + if(r->stat & (Err|Clkt)) { + qunlock(&i2c.lock); + error(Eio); + } + st = r->stat >> 28; + } while(st != 0 && st != 4 && st != 5); + } + } + else { + r->dlen = len; + r->ctrl = I2cen | Start | Intd | rw; + } + + p = buf; + st = rw == Read? Rxd : Txd; + while(len > 0){ + while((r->stat & (st|Done)) == 0){ + r->ctrl |= rw == Read? Intr : Intt; + sleep(&i2c.r, i2cready, (void*)(st|Done)); + } + if(r->stat & (Err|Clkt)){ + qunlock(&i2c.lock); + error(Eio); + } + if(rw == Read){ + do{ + *p++ = r->fifo; + len--; + }while ((r->stat & Rxd) && len > 0); + }else{ + do{ + r->fifo = *p++; + len--; + }while((r->stat & Txd) && len > 0); + } + } + while((r->stat & Done) == 0) + sleep(&i2c.r, i2cready, (void*)Done); + if(r->stat & (Err|Clkt)){ + qunlock(&i2c.lock); + error(Eio); + } + r->ctrl = 0; + qunlock(&i2c.lock); +} + + +void +i2csetup(int) +{ + //print("i2csetup\n"); + i2cinit(); +} + +long +i2csend(I2Cdev *d, void *buf, long len, ulong offset) +{ + i2cio(Write, d->tenbit, d->addr, buf, len, d->salen, offset); + return len; +} + +long +i2crecv(I2Cdev *d, void *buf, long len, ulong offset) +{ + i2cio(Read, d->tenbit, d->addr, buf, len, d->salen, offset); + return len; +} diff -Nru /n/sources/plan9/sys/src/9/bcm/io.h /sys/src/9/bcm/io.h --- /n/sources/plan9/sys/src/9/bcm/io.h Sun Nov 18 10:29:47 2012 +++ /sys/src/9/bcm/io.h Tue May 31 00:00:00 2016 @@ -8,11 +8,22 @@ IRQdma0 = 16, #define IRQDMA(chan) (IRQdma0+(chan)) IRQaux = 29, + IRQi2c = 53, + IRQspi = 54, IRQmmc = 62, IRQbasic = 64, IRQtimerArm = IRQbasic + 0, + IRQlocal = 96, + IRQcntps = IRQlocal + 0, + IRQcntpns = IRQlocal + 1, + IRQmbox0 = IRQlocal + 4, + IRQmbox1 = IRQlocal + 5, + IRQmbox2 = IRQlocal + 6, + IRQmbox3 = IRQlocal + 7, + IRQlocaltmr = IRQlocal + 11, + IRQfiq = IRQusb, /* only one source can be FIQ */ DmaD2M = 0, /* device to memory */ @@ -20,6 +31,11 @@ DmaM2M = 2, /* memory to memory */ DmaChanEmmc = 4, /* can only use 2-5, maybe 0 */ + DmaChanSpiTx= 2, + DmaChanSpiRx= 0, + + DmaDevSpiTx = 6, + DmaDevSpiRx = 7, DmaDevEmmc = 11, PowerSd = 0, diff -Nru /n/sources/plan9/sys/src/9/bcm/l.s /sys/src/9/bcm/l.s --- /n/sources/plan9/sys/src/9/bcm/l.s Thu Jan 3 12:17:09 2013 +++ /sys/src/9/bcm/l.s Tue May 31 00:00:00 2016 @@ -1,10 +1,14 @@ /* - * Broadcom bcm2835 SoC, as used in Raspberry Pi - * arm1176jzf-s processor (armv6) + * Common startup for armv6 and armv7 + * The rest of l.s has been moved to armv[67].s */ #include "arm.s" +/* + * on bcm2836, only cpu0 starts here + * other cpus enter at cpureset in armv7.s + */ TEXT _start(SB), 1, $-4 /* * load physical base for SB addressing while mmu is off @@ -16,259 +20,14 @@ 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 + * do arch-dependent startup (no return) */ - 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) + BL ,armstart(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 -Nru /n/sources/plan9/sys/src/9/bcm/lexception.s /sys/src/9/bcm/lexception.s --- /n/sources/plan9/sys/src/9/bcm/lexception.s Fri Jan 25 23:11:50 2013 +++ /sys/src/9/bcm/lexception.s Tue May 31 00:00:00 2016 @@ -27,6 +27,7 @@ WORD $_vfiq(SB) /* FIQ, switch to svc mode */ TEXT _vsvc(SB), 1, $-4 /* SWI */ + CLREX MOVW.W R14, -4(R13) /* ureg->pc = interrupted PC */ MOVW SPSR, R14 /* ureg->psr = SPSR */ MOVW.W R14, -4(R13) /* ... */ @@ -39,9 +40,16 @@ 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 */ + /* get R(MACH) for this cpu */ + CPUID(R1) + SLL $2, R1 /* convert to word index */ + MOVW $machaddr(SB), R2 + ADD R1, R2 + MOVW (R2), R(MACH) /* m = machaddr[cpuid] */ + CMP $0, R(MACH) + MOVW.EQ $MACHADDR, R0 /* paranoia: use MACHADDR if 0 */ + + MOVW 8(R(MACH)), R(USER) /* up */ MOVW R13, R0 /* first arg is pointer to ureg */ SUB $8, R13 /* space for argument+link */ @@ -81,6 +89,7 @@ * we'll switch to SVC mode and then call trap. */ _vswitch: + CLREX 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 */ @@ -119,7 +128,16 @@ BL trap(SB) + MOVW $setR12(SB), R12 /* reload kernel's SB (ORLY?) */ ADD $(4*2+4*15), R13 /* make r13 point to ureg->type */ + /* + * if we interrupted a previous trap's handler and are now + * returning to it, we need to propagate the current R(MACH) (R10) + * by overriding the saved one on the stack, since we may have + * been rescheduled and be on a different processor now than + * at entry. + */ + MOVW R(MACH), (-(15-MACH)*4)(R13) /* restore current cpu's MACH */ MOVW 8(R13), R14 /* restore link */ MOVW 4(R13), R0 /* restore SPSR */ MOVW R0, SPSR /* ... */ @@ -140,9 +158,16 @@ 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 */ + /* get R(MACH) for this cpu */ + CPUID(R1) + SLL $2, R1 /* convert to word index */ + MOVW $machaddr(SB), R2 + ADD R1, R2 + MOVW (R2), R(MACH) /* m = machaddr[cpuid] */ + CMP $0, R(MACH) + MOVW.EQ $MACHADDR, R(MACH) /* paranoia: use MACHADDR if 0 */ + + MOVW 8(R(MACH)), R(USER) /* up */ MOVW R13, R0 /* first arg is pointer to ureg */ SUB $(4*2), R13 /* space for argument+link (for debugger) */ @@ -158,14 +183,24 @@ RFE /* MOVM.IA.S.W (R13), [R15] */ TEXT _vfiq(SB), 1, $-4 /* FIQ */ + CLREX 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 */ + MOVM.DB.S [R0-R14], (R13) /* save interrupted regs */ + SUB $(15*4), R13 MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */ - MOVW $(MACHADDR), R10 /* m */ - MOVW 8(R10), R9 /* up */ + /* get R(MACH) for this cpu */ + CPUID(R1) + SLL $2, R1 /* convert to word index */ + MOVW $machaddr(SB), R2 + ADD R1, R2 + MOVW (R2), R(MACH) /* m = machaddr[cpuid] */ + CMP $0, R(MACH) + MOVW.EQ $MACHADDR, R(MACH) /* paranoia: use MACHADDR if 0 */ + + MOVW 8(R(MACH)), R(USER) /* up */ MOVW R13, R0 /* first arg is pointer to ureg */ SUB $(4*2), R13 /* space for argument+link (for debugger) */ @@ -187,6 +222,7 @@ MOVW CPSR, R2 BIC $PsrMask, R2, R3 + ORR $(PsrDirq|PsrDfiq), R3 ORR R0, R3 MOVW R3, CPSR /* switch to new mode */ diff -Nru /n/sources/plan9/sys/src/9/bcm/main.c /sys/src/9/bcm/main.c --- /n/sources/plan9/sys/src/9/bcm/main.c Fri Sep 20 21:38:39 2013 +++ /sys/src/9/bcm/main.c Tue May 31 00:00:00 2016 @@ -3,6 +3,7 @@ #include "../port/lib.h" #include "mem.h" #include "dat.h" +#include "io.h" #include "fns.h" #include "init.h" @@ -193,23 +194,92 @@ } } +/* enable scheduling of this cpu */ +void +machon(uint cpu) +{ + ulong cpubit; + + cpubit = 1 << cpu; + lock(&active); + if ((active.machs & cpubit) == 0) { /* currently off? */ + conf.nmach++; + active.machs |= cpubit; + } + unlock(&active); +} + +/* disable scheduling of this cpu */ +void +machoff(uint cpu) +{ + ulong cpubit; + + cpubit = 1 << cpu; + lock(&active); + if (active.machs & cpubit) { /* currently on? */ + conf.nmach--; + active.machs &= ~cpubit; + } + unlock(&active); +} + void machinit(void) { - m->machno = 0; - machaddr[m->machno] = m; + Mach *m0; m->ticks = 1; m->perf.period = 1; + m0 = MACHP(0); + if (m->machno != 0) { + /* synchronise with cpu 0 */ + m->ticks = m0->ticks; + } +} - conf.nmach = 1; +void +mach0init(void) +{ + conf.nmach = 0; - active.machs = 1; + m->machno = 0; + machaddr[m->machno] = m; + + machinit(); active.exiting = 0; up = nil; } +void +launchinit(int ncpus) +{ + int mach; + Mach *mm; + PTE *l1; + + if(ncpus > MAXMACH) + ncpus = MAXMACH; + for(mach = 1; mach < ncpus; mach++){ + machaddr[mach] = mm = mallocalign(MACHSIZE, MACHSIZE, 0, 0); + l1 = mallocalign(L1SIZE, L1SIZE, 0, 0); + if(mm == nil || l1 == nil) + panic("launchinit"); + memset(mm, 0, MACHSIZE); + mm->machno = mach; + + memmove(l1, m->mmul1, L1SIZE); /* clone cpu0's l1 table */ + cachedwbse(l1, L1SIZE); + mm->mmul1 = l1; + cachedwbse(mm, MACHSIZE); + + } + cachedwbse(machaddr, sizeof machaddr); + if((mach = startcpus(ncpus)) < ncpus) + panic("only %d cpu%s started", mach, mach == 1? "" : "s"); +} + static void optionsinit(char* s) { @@ -224,13 +294,13 @@ main(void) { extern char edata[], end[]; - uint rev; + uint fw, board; - okay(1); m = (Mach*)MACHADDR; memset(edata, 0, end - edata); /* clear bss */ - machinit(); - mmuinit1(); + mach0init(); + m->mmul1 = (PTE*)L1; + machon(0); optionsinit("/boot/boot boot"); quotefmtinstall(); @@ -242,14 +312,17 @@ screeninit(); print("\nPlan 9 from Bell Labs\n"); - rev = getfirmware(); - print("firmware: rev %d\n", rev); - if(rev < Minfirmrev){ + board = getboardrev(); + fw = getfirmware(); + print("board rev: %#ux firmware rev: %d\n", board, fw); + if(fw < Minfirmrev){ print("Sorry, firmware (start*.elf) must be at least rev %d" " or newer than %s\n", Minfirmrev, Minfirmdate); for(;;) ; } + /* set clock rate to arm_freq from config.txt (default pi1:700Mhz pi2:900MHz) */ + setclkrate(ClkArm, 0); trapinit(); clockinit(); printinit(); @@ -258,6 +331,7 @@ swcursorinit(); cpuidprint(); archreset(); + vgpinit(); procinit0(); initseg(); @@ -266,6 +340,9 @@ pageinit(); swapinit(); userinit(); + launchinit(getncpus()); + mmuinit1(); + schedinit(); assert(0); /* shouldn't have returned */ } @@ -277,6 +354,7 @@ init0(void) { int i; + Chan *c; char buf[2*KNAMELEN]; up->nerrlab = 0; @@ -310,6 +388,14 @@ ksetenv(confname[i], confval[i], 0); ksetenv(confname[i], confval[i], 1); } + if(getconf("pitft")){ + c = namec("#P/pitft", Aopen, OWRITE, 0); + if(!waserror()){ + devtab[c->type]->write(c, "init", 4, 0); + poperror(); + } + cclose(c); + } poperror(); } kproc("alarm", alarmkproc, 0); @@ -461,9 +547,6 @@ 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) @@ -474,7 +557,7 @@ conf.nswppo = 4096; conf.nimage = 200; - conf.copymode = 0; /* copy on write */ + conf.copymode = 1; /* copy on reference, not copy on write */ /* * Guess how much is taken by the large permanent @@ -514,15 +597,29 @@ active.exiting = 1; unlock(&active); - if(once) + if(once) { + delay(m->machno*100); /* stagger them */ iprint("cpu%d: exiting\n", m->machno); + } spllo(); - for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){ + if (m->machno == 0) + ms = 5*1000; + else + ms = 2*1000; + for(; ms > 0; ms -= TK2MS(2)){ delay(TK2MS(2)); if(active.machs == 0 && consactive() == 0) break; } - delay(1000); + if(active.ispanic){ + if(!cpuserver) + for(;;) + ; + if(getconf("*debug")) + delay(5*60*1000); + else + delay(10000); + } } /* @@ -531,9 +628,19 @@ void exit(int code) { + void (*f)(ulong, ulong, ulong); + shutdown(code); splfhi(); - archreboot(); + if(m->machno == 0) + archreboot(); + else{ + f = (void*)REBOOTADDR; + intrcpushutdown(); + cacheuwbinv(); + (*f)(0, 0, 0); + for(;;){} + } } /* @@ -542,9 +649,23 @@ int isaconfig(char *class, int ctlrno, ISAConf *isa) { - USED(ctlrno); - USED(isa); - return strcmp(class, "ether") == 0; + char cc[32], *p; + int i; + + if(strcmp(class, "ether") != 0) + return 0; + snprint(cc, sizeof cc, "%s%d", class, ctlrno); + p = getconf(cc); + if(p == nil) + return (ctlrno == 0); + isa->type = ""; + isa->nopt = tokenize(p, isa->opt, NISAOPT); + for(i = 0; i < isa->nopt; i++){ + p = isa->opt[i]; + if(cistrncmp(p, "type=", 5) == 0) + isa->type = p + 5; + } + return 1; } /* @@ -556,14 +677,31 @@ { void (*f)(ulong, ulong, ulong); - print("starting reboot..."); writeconf(); + + /* + * the boot processor is cpu0. execute this function on it + * so that the new kernel has the same cpu0. + */ + if (m->machno != 0) { + procwired(up, 0); + sched(); + } + if (m->machno != 0) + print("on cpu%d (not 0)!\n", m->machno); + + /* setup reboot trampoline function */ + f = (void*)REBOOTADDR; + memmove(f, rebootcode, sizeof(rebootcode)); + cachedwbse(f, sizeof(rebootcode)); + shutdown(0); /* * should be the only processor running now */ + delay(500); print("reboot entry %#lux code %#lux size %ld\n", PADDR(entry), PADDR(code), size); delay(100); @@ -574,29 +712,23 @@ screenputs = nil; /* shutdown devices */ - chandevshutdown(); + if(!waserror()){ + chandevshutdown(); + poperror(); + } /* stop the clock (and watchdog if any) */ clockshutdown(); splfhi(); - intrsoff(); - - /* setup reboot trampoline function */ - f = (void*)REBOOTADDR; - memmove(f, rebootcode, sizeof(rebootcode)); - cacheuwbinv(); + intrshutdown(); /* off we go - never to return */ + cacheuwbinv(); + l2cacheuwbinv(); (*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 -Nru /n/sources/plan9/sys/src/9/bcm/mem.h /sys/src/9/bcm/mem.h --- /n/sources/plan9/sys/src/9/bcm/mem.h Wed Feb 13 20:58:41 2013 +++ /sys/src/9/bcm/mem.h Tue May 31 00:00:00 2016 @@ -11,13 +11,21 @@ #define BY2PG (4*KiB) /* bytes per page */ #define PGSHIFT 12 /* log(BY2PG) */ -#define MAXMACH 1 /* max # cpus system can run */ +#define MAXMACH 4 /* max # cpus system can run */ #define MACHSIZE BY2PG +#define L1SIZE (4 * BY2PG) #define KSTKSIZE (8*KiB) #define STACKALIGN(sp) ((sp) & ~3) /* bug: assure with alloc */ /* + * Magic registers + */ + +#define USER 9 /* R9 is up-> */ +#define MACH 10 /* R10 is m-> */ + +/* * Address spaces. * KTZERO is used by kprof and dumpstack (if any). * @@ -28,8 +36,8 @@ */ #define KSEG0 0x80000000 /* kernel segment */ -/* mask to check segment; good for 512MB dram */ -#define KSEGM 0xE0000000 +/* mask to check segment; good for 1GB dram */ +#define KSEGM 0xC0000000 #define KZERO KSEG0 /* kernel address space */ #define CONFADDR (KZERO+0x100) /* unparsed plan9.ini */ #define MACHADDR (KZERO+0x2000) /* Mach structure */ @@ -39,23 +47,25 @@ #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 ARMLOCAL (VIRTIO+IOSIZE) /* armv7 only */ +#define VGPIO (ARMLOCAL+MiB) /* virtual gpio for pi3 ACT LED */ +#define FRAMEBUFFER 0xC0000000 /* video framebuffer */ #define UZERO 0 /* user segment */ #define UTZERO (UZERO+BY2PG) /* user text start */ #define UTROUND(t) ROUNDUP((t), BY2PG) -#define USTKTOP 0x20000000 /* user segment end +1 */ +#define USTKTOP 0x40000000 /* 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) +#define REBOOTADDR (KZERO+0x1800) /* * Legacy... */ -#define BLOCKALIGN 32 /* only used in allocb.c */ +#define BLOCKALIGN 64 /* only used in allocb.c */ #define KSTACK KSTKSIZE /* @@ -66,7 +76,6 @@ #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 @@ -88,8 +97,4 @@ * 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 -Nru /n/sources/plan9/sys/src/9/bcm/mkfile /sys/src/9/bcm/mkfile --- /n/sources/plan9/sys/src/9/bcm/mkfile Tue Mar 26 03:38:21 2013 +++ /sys/src/9/bcm/mkfile Tue May 31 00:00:00 2016 @@ -1,5 +1,5 @@ -CONF=pi -CONFLIST=pi picpu pifat +CONF=pi2 +CONFLIST=pi picpu pifat pi2 pi2cpu EXTRACOPIES= loadaddr=0x80008000 @@ -8,7 +8,7 @@ mmul1 = l1; + l1 = m->mmul1; /* * undo identity map of first MB of ram */ l1[L1X(PHYSDRAM)] = 0; - cachedwbse(&l1[L1X(PHYSDRAM)], sizeof(PTE)); - mmuinvalidate(); + cachedwbtlb(&l1[L1X(PHYSDRAM)], sizeof(PTE)); + mmuinvalidateaddr(PHYSDRAM); } static void @@ -80,10 +89,11 @@ l2 = &proc->mmul2; for(page = *l2; page != nil; page = page->next){ if(clear) - memset(UINT2PTR(page->va), 0, BY2PG); + memset(UINT2PTR(page->va), 0, L2size); l1[page->daddr] = Fault; l2 = &page->next; } + coherence(); *l2 = proc->mmul2cache; proc->mmul2cache = proc->mmul2; proc->mmul2 = nil; @@ -92,29 +102,24 @@ 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 > 0){ if(m->mmul1lo == 1) m->mmul1[L1lo] = Fault; else memset(&m->mmul1[L1lo], 0, m->mmul1lo*sizeof(PTE)); - m->mmul1lo = L1lo; + m->mmul1lo = 0; } - if(m->mmul1hi < L1hi){ - l1 = &m->mmul1[m->mmul1hi]; - if((L1hi - m->mmul1hi) == 1) + if(m->mmul1hi > 0){ + l1 = &m->mmul1[L1hi - m->mmul1hi]; + if(m->mmul1hi == 1) *l1 = Fault; else - memset(l1, 0, (L1hi - m->mmul1hi)*sizeof(PTE)); - m->mmul1hi = L1hi; + memset(l1, 0, m->mmul1hi*sizeof(PTE)); + m->mmul1hi = 0; } -#else - memset(&m->mmul1[L1lo], 0, (L1hi - L1lo)*sizeof(PTE)); -#endif /* notdef */ } void @@ -124,15 +129,7 @@ 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){ + if(proc != nil && proc->newtlb){ mmul2empty(proc, 1); proc->newtlb = 0; } @@ -141,19 +138,21 @@ /* move in new map */ l1 = m->mmul1; + if(proc != nil) 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; + if(x >= L1lo + m->mmul1lo && x < L1hi - m->mmul1hi){ + if(x+1 - L1lo < L1hi - x) + m->mmul1lo = x+1 - L1lo; + else + m->mmul1hi = L1hi - x; + } } /* make sure map is in memory */ /* could be smarter about how much? */ - cachedwbse(&l1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE)); + cachedwbtlb(&l1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE)); /* lose any possible stale tlb entries */ mmuinvalidate(); @@ -175,9 +174,6 @@ { 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; @@ -193,7 +189,7 @@ /* make sure map is in memory */ /* could be smarter about how much? */ - cachedwbse(&m->mmul1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE)); + cachedwbtlb(&m->mmul1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE)); /* lose any possible stale tlb entries */ mmuinvalidate(); @@ -202,39 +198,45 @@ void putmmu(uintptr va, uintptr pa, Page* page) { - int x; + int x, s; Page *pg; PTE *l1, *pte; + /* + * disable interrupts to prevent flushmmu (called from hzclock) + * from clearing page tables while we are setting them + */ + s = splhi(); x = L1X(va); l1 = &m->mmul1[x]; if(*l1 == Fault){ - /* wasteful - l2 pages only have 256 entries - fix */ + /* l2 pages only have 256 entries - wastes 3K per 1M of address space */ if(up->mmul2cache == nil){ - /* auxpg since we don't need much? memset if so */ + spllo(); pg = newpage(1, 0, 0); + splhi(); + /* if newpage slept, we might be on a different cpu */ + l1 = &m->mmul1[x]; pg->va = VA(kmap(pg)); - } - else{ + }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); + /* force l2 page to memory (armv6) */ + cachedwbtlb((void *)pg->va, L2size); *l1 = PPN(pg->pa)|Dom0|Coarse; - cachedwbse(l1, sizeof *l1); + cachedwbtlb(l1, sizeof *l1); - if(x >= m->mmul1lo && x < m->mmul1hi){ - if(x+1 - m->mmul1lo < m->mmul1hi - x) - m->mmul1lo = x+1; + if(x >= L1lo + m->mmul1lo && x < L1hi - m->mmul1hi){ + if(x+1 - L1lo < L1hi - x) + m->mmul1lo = x+1 - L1lo; else - m->mmul1hi = x; + m->mmul1hi = L1hi - x; } } pte = UINT2PTR(KADDR(PPN(*l1))); @@ -246,30 +248,51 @@ */ x = Small; if(!(pa & PTEUNCACHED)) - x |= Cached|Buffered; + x |= L2ptedramattrs; if(pa & PTEWRITE) x |= L2AP(Urw); else x |= L2AP(Uro); pte[L2X(va)] = PPN(pa)|x; - cachedwbse(&pte[L2X(va)], sizeof pte[0]); + cachedwbtlb(&pte[L2X(va)], sizeof(PTE)); /* 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){ + if(page->cachectl[m->machno] == PG_TXTFLUSH){ /* pio() sets PG_TXTFLUSH whenever a text pg has been written */ - cacheiinv(); - page->cachectl[0] = PG_NOFLUSH; + cachedwbse((void*)(page->pa|KZERO), BY2PG); + cacheiinvse((void*)page->va, BY2PG); + page->cachectl[m->machno] = PG_NOFLUSH; } - checkmmu(va, PPN(pa)); + //checkmmu(va, PPN(pa)); + splx(s); +} + +void* +mmuuncache(void* v, usize size) +{ + int x; + PTE *pte; + uintptr va; + + /* + * Simple helper for ucalloc(). + * Uncache a Section, must already be + * valid in the MMU. + */ + va = PTR2UINT(v); + assert(!(va & (1*MiB-1)) && size == 1*MiB); + + x = L1X(va); + pte = &m->mmul1[x]; + if((*pte & (Fine|Section|Coarse)) != Section) + return nil; + *pte &= ~L1ptedramattrs; + mmuinvalidateaddr(va); + cachedwbinvse(pte, 4); + + return v; } /* @@ -304,15 +327,31 @@ *pte++ = (pa+n)|Dom0|L1AP(Krw)|Section; mmuinvalidateaddr(va+n); } - cachedwbse(pte0, pte - pte0); + cachedwbtlb(pte0, (uintptr)pte - (uintptr)pte0); return va + o; } - void checkmmu(uintptr va, uintptr pa) { - USED(va); - USED(pa); + int x; + PTE *l1, *pte; + + x = L1X(va); + l1 = &m->mmul1[x]; + if(*l1 == Fault){ + iprint("checkmmu cpu%d va=%lux l1 %p=%ux\n", m->machno, va, l1, *l1); + return; + } + pte = KADDR(PPN(*l1)); + pte += L2X(va); + if(pa == ~0 || (pa != 0 && PPN(*pte) != pa)) + iprint("checkmmu va=%lux pa=%lux l1 %p=%ux pte %p=%ux\n", va, pa, l1, *l1, pte, *pte); } +void +kunmap(KMap *k) +{ + USED(k); + coherence(); +} diff -Nru /n/sources/plan9/sys/src/9/bcm/pi /sys/src/9/bcm/pi --- /n/sources/plan9/sys/src/9/bcm/pi Fri Jan 25 22:10:17 2013 +++ /sys/src/9/bcm/pi Tue May 31 00:00:00 2016 @@ -18,11 +18,15 @@ kbmap kbin kbd latin1 uart + gpio gpio + spi spi + i2c i2c fakertc +# rtc3231 i2c + ether netif sd usb - ether netif link archbcm @@ -30,6 +34,7 @@ ethermedium usbdwc etherusb + pitft ip tcp @@ -40,11 +45,13 @@ ipmux misc - uartmini + armv6 + uartmini gpio sdmmc emmc dma vcore vfp3 coproc +# spi port int cpuserver = 0; diff -Nru /n/sources/plan9/sys/src/9/bcm/pi2 /sys/src/9/bcm/pi2 --- /n/sources/plan9/sys/src/9/bcm/pi2 Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/pi2 Tue May 31 00:00:00 2016 @@ -0,0 +1,68 @@ +dev + root + cons + env + pipe + proc + mnt + srv + dup + arch + ssl + tls + cap + fs + ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno + draw screen + mouse mouse + kbmap + kbin kbd latin1 + uart + gpio gpio + spi spi + i2c i2c + + fakertc +# rtc3231 i2c + ether netif + sd + usb + +link + archbcm2 + loopbackmedium + ethermedium + usbdwc + etherusb + pitft + +ip + tcp + udp + ipifc + icmp + icmp6 + ipmux + +misc + armv7 + uartmini gpio + sdmmc emmc + dma + vcore + vfp3 coproc +# spi + +port + int cpuserver = 0; + +boot boot #S/sdM0/ + local + tcp + +bootdir + boot$CONF.out boot + /arm/bin/ip/ipconfig + /arm/bin/auth/factotum + /arm/bin/fossil/fossil + /arm/bin/usb/usbd diff -Nru /n/sources/plan9/sys/src/9/bcm/pi2cpu /sys/src/9/bcm/pi2cpu --- /n/sources/plan9/sys/src/9/bcm/pi2cpu Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/pi2cpu Tue May 31 00:00:00 2016 @@ -0,0 +1,68 @@ +dev + root + cons + env + pipe + proc + mnt + srv + dup + arch + ssl + tls + cap + fs + ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno + draw screen + mouse mouse + kbmap + kbin kbd latin1 + uart + gpio gpio + spi spi + i2c i2c + + fakertc +# rtc3231 i2c + ether netif + sd + usb + +link + archbcm2 + loopbackmedium + ethermedium + usbdwc + etherusb + pitft + +ip + tcp + udp + ipifc + icmp + icmp6 + ipmux + +misc + armv7 + uartmini gpio + sdmmc emmc + dma + vcore + vfp3 coproc +# spi + +port + int cpuserver = 1; + +boot cpu boot #S/sdM0/ + local + tcp + +bootdir + boot$CONF.out boot + /arm/bin/ip/ipconfig + /arm/bin/auth/factotum + /arm/bin/fossil/fossil + /arm/bin/usb/usbd diff -Nru /n/sources/plan9/sys/src/9/bcm/picpu /sys/src/9/bcm/picpu --- /n/sources/plan9/sys/src/9/bcm/picpu Fri Jan 25 22:13:25 2013 +++ /sys/src/9/bcm/picpu Tue May 31 00:00:00 2016 @@ -18,11 +18,15 @@ kbmap kbin kbd latin1 uart + gpio gpio + spi spi + i2c i2c fakertc +# rtc3231 i2c + ether netif sd usb - ether netif link archbcm @@ -30,6 +34,7 @@ ethermedium usbdwc etherusb + pitft ip tcp @@ -40,11 +45,13 @@ ipmux misc - uartmini + armv6 + uartmini gpio sdmmc emmc dma vcore vfp3 coproc +# spi port int cpuserver = 1; diff -Nru /n/sources/plan9/sys/src/9/bcm/pifat /sys/src/9/bcm/pifat --- /n/sources/plan9/sys/src/9/bcm/pifat Fri Jan 25 22:13:38 2013 +++ /sys/src/9/bcm/pifat Tue May 31 00:00:00 2016 @@ -33,7 +33,8 @@ ipmux misc - uartmini + armv6 + uartmini gpio sdmmc emmc dma vfp3 coproc diff -Nru /n/sources/plan9/sys/src/9/bcm/pitft.c /sys/src/9/bcm/pitft.c --- /n/sources/plan9/sys/src/9/bcm/pitft.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/pitft.c Tue May 31 00:00:00 2016 @@ -0,0 +1,277 @@ +/* + * Support for a SPI LCD panel from Adafruit + * based on HX8357D controller chip + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +enum { + TFTWidth = 480, + TFTHeight = 320, +}; + +static void pitftblank(int); +static void pitftdraw(Rectangle); +static long pitftread(Chan*, void*, long, vlong); +static long pitftwrite(Chan*, void*, long, vlong); +static void spicmd(uchar); +static void spidata(uchar *, int); +static void setwindow(int, int, int, int); +static void xpitftdraw(void *); + +extern Memimage xgscreen; +extern Lcd *lcd; + +static Lcd pitft = { + pitftdraw, + pitftblank, +}; + +static Queue *updateq = nil; + +void +pitftlink(void) +{ + addarchfile("pitft", 0666, pitftread, pitftwrite); +} + +static void +pitftsetup(void) +{ + uchar spibuf[32]; + + gpiosel(25, Output); + spirw(0, spibuf, 1); + spicmd(0x01); + delay(10); + spicmd(0x11); + delay(10); + spicmd(0x29); + spicmd(0x13); + spicmd(0x36); + spibuf[0] = 0xe8; + spidata(spibuf, 1); + spicmd(0x3a); + spibuf[0] = 0x05; + spidata(spibuf, 1); +} + +static long +pitftread(Chan *, void *, long, vlong) +{ + return 0; +} + +static long +pitftwrite(Chan *, void *a, long n, vlong) +{ + if(strncmp(a, "init", 4) == 0 && updateq == nil) { + /* + * The HX8357 datasheet shows minimum + * clock cycle time of 66nS but the clock high + * and low times as 15nS and it seems to + * work at around 32MHz. + */ + spiclock(32); + pitftsetup(); + updateq = qopen(16384, 1, nil, nil); + kproc("pitft", xpitftdraw, nil); + lcd = &pitft; + } + return n; +} + +static void +pitftblank(int blank) +{ + USED(blank); +} + +static void +pitftdraw(Rectangle r) +{ + if(updateq == nil) + return; + if(r.min.x > TFTWidth || r.min.y > TFTHeight) + return; + /* + * using qproduce to make sure we don't block + * but if we've got a lot on the queue, it means we're + * redrawing the same areas over and over; clear it + * out and just draw the whole screen once + */ + if(qproduce(updateq, &r, sizeof(Rectangle)) == -1) { + r = Rect(0, 0, TFTWidth, TFTHeight); + qflush(updateq); + qproduce(updateq, &r, sizeof(Rectangle)); + } +} + +int +overlap(Rectangle r1, Rectangle r2) +{ + if(r1.max.x < r2.min.x) + return 0; + if(r1.min.x > r2.max.x) + return 0; + if(r1.max.y < r2.min.y) + return 0; + if(r1.min.y > r2.max.y) + return 0; + return 1; +} + +int +min(int x, int y) +{ + if(x < y) + return x; + return y; +} + +int +max(int x, int y) +{ + if(x < y) + return y; + return x; +} + +/* + * Because everyone wants to be holding locks when + * they update the screen but we need to sleep in the + * SPI code, we're decoupling this into a separate kproc(). + */ +static void +xpitftdraw(void *) +{ + Rectangle rec, bb; + Point pt; + uchar *p; + int i, r, c, gotrec; + uchar spibuf[32]; + + gotrec = 0; + qread(updateq, &rec, sizeof(Rectangle)); + bb = Rect(0, 0, TFTWidth, TFTHeight); + while(1) { + setwindow(bb.min.x, bb.min.y, + bb.max.x-1, bb.max.y-1); + spicmd(0x2c); + for(r = bb.min.y; r < bb.max.y; ++r) { + for(c = bb.min.x; c < bb.max.x; c += 8) { + for(i = 0; i < 8; ++i) { + pt.y = r; + pt.x = c + i; + p = byteaddr(&xgscreen, pt); + switch(xgscreen.depth) { + case 16: // RGB16 + spibuf[i*2+1] = p[0]; + spibuf[i*2] = p[1]; + break; + case 24: // BGR24 + spibuf[i*2] = (p[2] & 0xf8) | + (p[1] >> 5); + spibuf[i*2+1] = (p[0] >> 3) | + (p[1] << 3); + break; + case 32: // ARGB32 + spibuf[i*2] = (p[0] & 0xf8) | + (p[1] >> 5); + spibuf[i*2+1] = (p[1] >> 3) | + (p[1] << 3); + break; + } + } + spidata(spibuf, 16); + } + } + bb.max.y = -1; + while(1) { + if(!gotrec) { + qread(updateq, &rec, sizeof(Rectangle)); + gotrec = 1; + } + if(bb.max.y != -1) { + if(!overlap(bb, rec)) + break; + rec.min.x = min(rec.min.x, bb.min.x); + rec.min.y = min(rec.min.y, bb.min.y); + rec.max.x = max(rec.max.x, bb.max.x); + rec.max.y = max(rec.max.y, bb.max.y); + } + gotrec = 0; + // Expand rows to 8 pixel alignment + bb.min.x = rec.min.x & ~7; + if(bb.min.x < 0) + bb.min.x = 0; + bb.max.x = (rec.max.x + 7) & ~7; + if(bb.max.x > TFTWidth) + bb.max.x = TFTWidth; + bb.min.y = rec.min.y; + if(bb.min.y < 0) + bb.min.y = 0; + bb.max.y = rec.max.y; + if(bb.max.y > TFTHeight) + bb.max.y = TFTHeight; + if(qcanread(updateq)) { + qread(updateq, &rec, sizeof(Rectangle)); + gotrec = 1; + } + else + break; + } + } +} + +static void +spicmd(uchar c) +{ + char buf; + + gpioout(25, 0); + buf = c; + spirw(0, &buf, 1); +} + +static void +spidata(uchar *p, int n) +{ + char buf[128]; + + if(n > 128) + n = 128; + gpioout(25, 1); + memmove(buf, p, n); + spirw(0, buf, n); + gpioout(25, 0); +} + +static void +setwindow(int minc, int minr, int maxc, int maxr) +{ + uchar spibuf[4]; + + spicmd(0x2a); + spibuf[0] = minc >> 8; + spibuf[1] = minc & 0xff; + spibuf[2] = maxc >> 8; + spibuf[3] = maxc & 0xff; + spidata(spibuf, 4); + spicmd(0x2b); + spibuf[0] = minr >> 8; + spibuf[1] = minr & 0xff; + spibuf[2] = maxr >> 8; + spibuf[3] = maxr & 0xff; + spidata(spibuf, 4); +} diff -Nru /n/sources/plan9/sys/src/9/bcm/rebootcode.s /sys/src/9/bcm/rebootcode.s --- /n/sources/plan9/sys/src/9/bcm/rebootcode.s Fri Dec 28 19:39:36 2012 +++ /sys/src/9/bcm/rebootcode.s Tue May 31 00:00:00 2016 @@ -1,8 +1,13 @@ /* - * armv6 reboot code + * armv6/armv7 reboot code */ #include "arm.s" +#define PTEDRAM (Dom0|L1AP(Krw)|Section) + +#define WFI WORD $0xe320f003 /* wait for interrupt */ +#define WFE WORD $0xe320f002 /* wait for event */ + /* * Turn off MMU, then copy the new kernel to its correct location * in physical memory. Then jump to the start of the kernel. @@ -15,7 +20,7 @@ /* 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 */ + MOVW n+8(FP), R6 /* byte count */ /* SVC mode, interrupts disabled */ MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 @@ -29,6 +34,28 @@ BIC $CpCmmu, R1 MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + /* continue with reboot only on cpu0 */ + CPUID(R2) + BEQ bootcpu + + /* other cpus wait for inter processor interrupt from cpu0 */ + /* turn icache back on */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + ORR $(CpCicache), R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + BARRIERS +dowfi: + WFI + MOVW $0x40000060, R1 + ADD R2<<2, R1 + MOVW 0(R1), R0 + AND $0x10, R0 + BEQ dowfi + MOVW $0x8000, R1 + BL (R1) + B dowfi + +bootcpu: /* 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 */ @@ -37,11 +64,12 @@ 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 */ + MOVW R6, 12(SP) /* push size */ BL memmove(SB) MOVW 16(SP), R8 /* restore entry point */ /* jump to kernel physical entry point */ + ORR R8,R8 B (R8) B 0(PC) @@ -51,43 +79,40 @@ * clobbers R0-R2, and returns with SP invalid. */ TEXT cachesoff(SB), 1, $-4 + MOVM.DB.W [R14,R1-R10], (R13) /* save regs on stack */ - /* 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 */ + /* turn caches off, invalidate icache */ MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl BIC $(CpCdcache|CpCicache|CpCpredict), R1 MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + MOVW $0, R0 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall /* invalidate stale TLBs before changing them */ BARRIERS - MOVW $KZERO, R0 /* some valid virtual address */ + MOVW $0, R0 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 12(R(MACH)), R2 /* m->mmul1 (virtual addr) */ MOVW $PTEDRAM, R1 /* PTE bits */ - ORR R0, R1 /* dram base */ MOVW R1, (R2) + DSB + MCR CpSC, 0, R2, C(CpCACHE), C(CpCACHEwb), CpCACHEse /* invalidate stale TLBs again */ BARRIERS + MOVW $0, R0 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 + MOVM.IA.W (R13), [R14,R1-R10] /* restore regs from stack */ + + MOVW $KSEGM, R1 /* clear segment bits */ BIC R1, R14 /* adjust return address */ - ORR R0, R14 RET diff -Nru /n/sources/plan9/sys/src/9/bcm/screen.c /sys/src/9/bcm/screen.c --- /n/sources/plan9/sys/src/9/bcm/screen.c Tue Jan 29 22:07:19 2013 +++ /sys/src/9/bcm/screen.c Tue May 31 00:00:00 2016 @@ -37,10 +37,11 @@ }; Memimage *gscreen; +Lcd *lcd; static Memdata xgdata; -static Memimage xgscreen = +/*static*/ Memimage xgscreen = { { 0, 0, Wid, Ht }, /* r */ { 0, 0, Wid, Ht }, /* clipr */ @@ -287,18 +288,24 @@ if(mask->data->bdata == xgdata.bdata) swcursoravoid(par->mr); + if(lcd) + lcd->draw(par->r); + return 0; } static int screensize(void) { - char *p; + char *p, buf[32]; char *f[3]; int width, height, depth; p = getconf("vgasize"); - if(p == nil || getfields(p, f, nelem(f), 0, "x") != nelem(f) || + if(p == nil || memccpy(buf, p, '\0', sizeof buf) == nil) + return -1; + + if(getfields(buf, f, nelem(f), 0, "x") != nelem(f) || (width = atoi(f[0])) < 16 || (height = atoi(f[1])) <= 0 || (depth = atoi(f[2])) <= 0) @@ -312,7 +319,8 @@ screeninit(void) { uchar *fb; - int set; + char *p; + int set, rgbswap; ulong chan; set = screensize() == 0; @@ -322,6 +330,7 @@ xgscreen.r.max.x, xgscreen.r.max.y, xgscreen.depth); return; } + rgbswap = ((p = getconf("bcm2708_fb.fbswap")) != nil && *p == '1'); xgscreen.clipr = xgscreen.r; switch(xgscreen.depth){ default: @@ -332,7 +341,7 @@ chan = RGB16; break; case 24: - chan = BGR24; + chan = rgbswap? RGB24 : BGR24; break; case 32: chan = ARGB32; @@ -385,6 +394,8 @@ blankscreen(int blank) { fbblank(blank); + if(lcd) + lcd->blank(blank); } static void @@ -471,9 +482,13 @@ p = Pt(window.min.x, window.min.y+o); memimagedraw(gscreen, r, gscreen, p, nil, p, S); flushmemscreen(r); + if(lcd) + lcd->draw(r); r = Rpt(Pt(window.min.x, window.max.y-o), window.max); memimagedraw(gscreen, r, back, ZP, nil, ZP, S); flushmemscreen(r); + if(lcd) + lcd->draw(r); curpos.y -= o; } diff -Nru /n/sources/plan9/sys/src/9/bcm/screen.h /sys/src/9/bcm/screen.h --- /n/sources/plan9/sys/src/9/bcm/screen.h Fri Jan 25 22:13:03 2013 +++ /sys/src/9/bcm/screen.h Tue May 31 00:00:00 2016 @@ -1,8 +1,15 @@ typedef struct Cursor Cursor; typedef struct Cursorinfo Cursorinfo; +typedef struct Lcd Lcd; + struct Cursorinfo { Cursor; Lock; +}; + +struct Lcd { + void (*draw)(Rectangle); + void (*blank)(int); }; /* devmouse.c */ diff -Nru /n/sources/plan9/sys/src/9/bcm/spi.c /sys/src/9/bcm/spi.c --- /n/sources/plan9/sys/src/9/bcm/spi.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/spi.c Tue May 31 00:00:00 2016 @@ -0,0 +1,149 @@ +/* + * bcm2835 spi controller + */ + +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define SPIREGS (VIRTIO+0x204000) +#define SPI0_CE1_N 7 /* P1 pin 26 */ +#define SPI0_CE0_N 8 /* P1 pin 24 */ +#define SPI0_MISO 9 /* P1 pin 21 */ +#define SPI0_MOSI 10 /* P1 pin 19 */ +#define SPI0_SCLK 11 /* P1 pin 23 */ + +typedef struct Ctlr Ctlr; +typedef struct Spiregs Spiregs; + +/* + * Registers for main SPI controller + */ +struct Spiregs { + u32int cs; /* control and status */ + u32int data; + u32int clkdiv; + u32int dlen; + u32int lossitoh; + u32int dmactl; +}; + +/* + * Per-controller info + */ +struct Ctlr { + Spiregs *regs; + QLock lock; + Lock reglock; + Rendez r; +}; + +static Ctlr spi; + +enum { + /* cs */ + Lossi32bit = 1<<25, + Lossidma = 1<<24, + Cspol2 = 1<<23, + Cspol1 = 1<<22, + Cspol0 = 1<<21, + Rxf = 1<<20, + Rxr = 1<<19, + Txd = 1<<18, + Rxd = 1<<17, + Done = 1<<16, + Lossi = 1<<13, + Ren = 1<<12, + Adcs = 1<<11, /* automatically deassert chip select (dma) */ + Intr = 1<<10, + Intd = 1<<9, + Dmaen = 1<<8, + Ta = 1<<7, + Cspol = 1<<6, + Rxclear = 1<<5, + Txclear = 1<<4, + Cpol = 1<<3, + Cpha = 1<<2, + Csmask = 3<<0, + Csshift = 0, + + /* dmactl */ + Rpanicshift = 24, + Rdreqshift = 16, + Tpanicshift = 8, + Tdreqshift = 0, +}; + +static void +spiinit(void) +{ + spi.regs = (Spiregs*)SPIREGS; + spi.regs->clkdiv = 250; /* 1 MHz */ + gpiosel(SPI0_MISO, Alt0); + gpiosel(SPI0_MOSI, Alt0); + gpiosel(SPI0_SCLK, Alt0); + gpiosel(SPI0_CE0_N, Alt0); + gpiosel(SPI0_CE1_N, Alt0); +} + +void +spimode(int mode) +{ + if(spi.regs == 0) + spiinit(); + spi.regs->cs = (spi.regs->cs & ~(Cpha | Cpol)) | (mode << 2); +} + +/* + * According the Broadcom docs, the divisor has to + * be a power of 2, but an errata says that should + * be multiple of 2 and scope observations confirm + * that restricting it to a power of 2 is unnecessary. + */ +void +spiclock(uint mhz) +{ + if(spi.regs == 0) + spiinit(); + if(mhz == 0) { + spi.regs->clkdiv = 32768; /* about 8 KHz */ + return; + } + spi.regs->clkdiv = 2 * ((125 + (mhz - 1)) / mhz); +} + +void +spirw(uint cs, void *buf, int len) +{ + Spiregs *r; + + assert(cs <= 2); + assert(len < (1<<16)); + qlock(&spi.lock); + if(waserror()){ + qunlock(&spi.lock); + nexterror(); + } + if(spi.regs == 0) + spiinit(); + r = spi.regs; + r->dlen = len; + r->cs = (r->cs & (Cpha | Cpol)) | (cs << Csshift) | Rxclear | Txclear | Dmaen | Adcs | Ta; + /* + * Start write channel before read channel - cache wb before inv + */ + dmastart(DmaChanSpiTx, DmaDevSpiTx, DmaM2D, + buf, &r->data, len); + dmastart(DmaChanSpiRx, DmaDevSpiRx, DmaD2M, + &r->data, buf, len); + if(dmawait(DmaChanSpiRx) < 0) + error(Eio); + cachedinvse(buf, len); + r->cs &= (Cpha | Cpol); + qunlock(&spi.lock); + poperror(); +} diff -Nru /n/sources/plan9/sys/src/9/bcm/taslock.c /sys/src/9/bcm/taslock.c --- /n/sources/plan9/sys/src/9/bcm/taslock.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/taslock.c Tue May 31 00:00:00 2016 @@ -0,0 +1,257 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../port/edf.h" + +long maxlockcycles; +long maxilockcycles; +long cumlockcycles; +long cumilockcycles; +ulong maxlockpc; +ulong maxilockpc; + +struct +{ + ulong locks; + ulong glare; + ulong inglare; +} lockstats; + +static void +inccnt(Ref *r) +{ + _xinc(&r->ref); +} + +static int +deccnt(Ref *r) +{ + int x; + + x = _xdec(&r->ref); + if(x < 0) + panic("deccnt pc=%#p", getcallerpc(&r)); + return x; +} + +static void +dumplockmem(char *tag, Lock *l) +{ + uchar *cp; + int i; + + iprint("%s: ", tag); + cp = (uchar*)l; + for(i = 0; i < 64; i++) + iprint("%2.2ux ", cp[i]); + iprint("\n"); +} + +void +lockloop(Lock *l, ulong pc) +{ + Proc *p; + + p = l->p; + print("lock %#p loop key %#lux pc %#lux held by pc %#lux proc %lud\n", + l, l->key, pc, l->pc, p ? p->pid : 0); + dumpaproc(up); + if(p != nil) + dumpaproc(p); +} + +int +lock(Lock *l) +{ + int i; + ulong pc; + + pc = getcallerpc(&l); + + lockstats.locks++; + if(up) + inccnt(&up->nlocks); /* prevent being scheded */ + if(tas(&l->key) == 0){ + if(up) + up->lastlock = l; + l->pc = pc; + l->p = up; + l->isilock = 0; +#ifdef LOCKCYCLES + l->lockcycles = -lcycles(); +#endif + return 0; + } + if(up) + deccnt(&up->nlocks); + + lockstats.glare++; + for(;;){ + lockstats.inglare++; + i = 0; + while(l->key){ + if(conf.nmach < 2 && up && up->edf && (up->edf->flags & Admitted)){ + /* + * Priority inversion, yield on a uniprocessor; on a + * multiprocessor, the other processor will unlock + */ + print("inversion %#p pc %#lux proc %lud held by pc %#lux proc %lud\n", + l, pc, up ? up->pid : 0, l->pc, l->p ? l->p->pid : 0); + up->edf->d = todget(nil); /* yield to process with lock */ + } + if(i++ > 100000000){ + i = 0; + lockloop(l, pc); + } + } + if(up) + inccnt(&up->nlocks); + if(tas(&l->key) == 0){ + if(up) + up->lastlock = l; + l->pc = pc; + l->p = up; + l->isilock = 0; +#ifdef LOCKCYCLES + l->lockcycles = -lcycles(); +#endif + return 1; + } + if(up) + deccnt(&up->nlocks); + } +} + +void +ilock(Lock *l) +{ + ulong x; + ulong pc; + + pc = getcallerpc(&l); + lockstats.locks++; + + x = splhi(); + if(tas(&l->key) != 0){ + lockstats.glare++; + /* + * Cannot also check l->pc, l->m, or l->isilock here + * because they might just not be set yet, or + * (for pc and m) the lock might have just been unlocked. + */ + for(;;){ + lockstats.inglare++; + splx(x); + while(l->key) + ; + x = splhi(); + if(tas(&l->key) == 0) + goto acquire; + } + } +acquire: + m->ilockdepth++; + if(up) + up->lastilock = l; + l->sr = x; + l->pc = pc; + l->p = up; + l->isilock = 1; + l->m = MACHP(m->machno); +#ifdef LOCKCYCLES + l->lockcycles = -lcycles(); +#endif +} + +int +canlock(Lock *l) +{ + if(up) + inccnt(&up->nlocks); + if(tas(&l->key)){ + if(up) + deccnt(&up->nlocks); + return 0; + } + + if(up) + up->lastlock = l; + l->pc = getcallerpc(&l); + l->p = up; + l->m = MACHP(m->machno); + l->isilock = 0; +#ifdef LOCKCYCLES + l->lockcycles = -lcycles(); +#endif + return 1; +} + +void +unlock(Lock *l) +{ +#ifdef LOCKCYCLES + l->lockcycles += lcycles(); + cumlockcycles += l->lockcycles; + if(l->lockcycles > maxlockcycles){ + maxlockcycles = l->lockcycles; + maxlockpc = l->pc; + } +#endif + if(l->key == 0) + print("unlock: not locked: pc %#p\n", getcallerpc(&l)); + if(l->isilock) + print("unlock of ilock: pc %lux, held by %lux\n", getcallerpc(&l), l->pc); + if(l->p != up) + print("unlock: up changed: pc %#p, acquired at pc %lux, lock p %#p, unlock up %#p\n", getcallerpc(&l), l->pc, l->p, up); + l->m = nil; + coherence(); + l->key = 0; + coherence(); + + if(up && deccnt(&up->nlocks) == 0 && up->delaysched && islo()){ + /* + * Call sched if the need arose while locks were held + * But, don't do it from interrupt routines, hence the islo() test + */ + sched(); + } +} + +ulong ilockpcs[0x100] = { [0xff] = 1 }; +static int n; + +void +iunlock(Lock *l) +{ + ulong sr; + +#ifdef LOCKCYCLES + l->lockcycles += lcycles(); + cumilockcycles += l->lockcycles; + if(l->lockcycles > maxilockcycles){ + maxilockcycles = l->lockcycles; + maxilockpc = l->pc; + } + if(l->lockcycles > 2400) + ilockpcs[n++ & 0xff] = l->pc; +#endif + if(l->key == 0) + print("iunlock: not locked: pc %#p\n", getcallerpc(&l)); + if(!l->isilock) + print("iunlock of lock: pc %#p, held by %#lux\n", getcallerpc(&l), l->pc); + if(islo()) + print("iunlock while lo: pc %#p, held by %#lux\n", getcallerpc(&l), l->pc); + + sr = l->sr; + l->m = nil; + coherence(); + l->key = 0; + coherence(); + m->ilockdepth--; + if(up) + up->lastilock = nil; + splx(sr); +} diff -Nru /n/sources/plan9/sys/src/9/bcm/trap.c /sys/src/9/bcm/trap.c --- /n/sources/plan9/sys/src/9/bcm/trap.c Tue Mar 26 20:33:31 2013 +++ /sys/src/9/bcm/trap.c Tue May 31 00:00:00 2016 @@ -13,6 +13,7 @@ #include "arm.h" #define INTREGS (VIRTIO+0xB200) +#define LOCALREGS (VIRTIO+IOSIZE) typedef struct Intregs Intregs; typedef struct Vctl Vctl; @@ -22,6 +23,10 @@ Nvec = 8, /* # of vectors at start of lexception.s */ Fiqenable = 1<<7, + + Localtimerint = 0x40, + Localmboxint = 0x50, + Localintpending = 0x60, }; /* @@ -48,12 +53,14 @@ struct Vctl { Vctl *next; int irq; + int cpu; u32int *reg; u32int mask; void (*f)(Ureg*, void*); void *a; }; +static Lock vctllock; static Vctl *vctl, *vfiq; static char *trapnames[PsrMask+1] = { @@ -77,14 +84,16 @@ { 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(); + if (m->machno == 0) { + /* 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(); + l2cacheuwbinv(); + } /* set up the stacks for the interrupt modes */ setr13(PsrMfiq, (u32int*)(FIQSTKTOP)); @@ -97,6 +106,21 @@ } void +intrcpushutdown(void) +{ + u32int *enable; + + if(soc.armlocal == 0) + return; + enable = (u32int*)(LOCALREGS + Localtimerint) + m->machno; + *enable = 0; + if(m->machno){ + enable = (u32int*)(LOCALREGS + Localmboxint) + m->machno; + *enable = 1; + } +} + +void intrsoff(void) { Intregs *ip; @@ -110,6 +134,30 @@ ip->FIQctl = 0; } +/* called from cpu0 after other cpus are shutdown */ +void +intrshutdown(void) +{ + intrsoff(); + intrcpushutdown(); +} + +static void +intrtime(void) +{ + ulong diff; + ulong x; + + x = perfticks(); + diff = x - m->perf.intrts; + m->perf.intrts = 0; + + m->perf.inintr += diff; + if(up == nil && m->perf.inidle > diff) + m->perf.inidle -= diff; +} + + /* * called by trap to handle irq interrupts. * returns true iff a clock interrupt, thus maybe reschedule. @@ -119,16 +167,23 @@ { Vctl *v; int clockintr; + int found; + m->perf.intrts = perfticks(); clockintr = 0; + found = 0; for(v = vctl; v; v = v->next) - if(*v->reg & v->mask){ + if(v->cpu == m->machno && (*v->reg & v->mask) != 0){ + found = 1; coherence(); v->f(ureg, v->a); coherence(); - if(v->irq == IRQclock) + if(v->irq == IRQclock || v->irq == IRQcntps || v->irq == IRQcntpns) clockintr = 1; } + if(!found) + m->spuriousintr++; + intrtime(); return clockintr; } @@ -139,15 +194,24 @@ fiq(Ureg *ureg) { Vctl *v; + int inintr; + if(m->perf.intrts) + inintr = 1; + else{ + inintr = 0; + m->perf.intrts = perfticks(); + } v = vfiq; if(v == nil) - panic("unexpected item in bagging area"); + panic("cpu%d: unexpected item in bagging area", m->machno); m->intr++; ureg->pc -= 4; coherence(); v->f(ureg, v->a); coherence(); + if(!inintr) + intrtime(); } void @@ -162,7 +226,16 @@ if(v == nil) panic("irqenable: no mem"); v->irq = irq; - if(irq >= IRQbasic){ + v->cpu = 0; + if(irq >= IRQlocal){ + v->reg = (u32int*)(LOCALREGS + Localintpending) + m->machno; + if(irq >= IRQmbox0) + enable = (u32int*)(LOCALREGS + Localmboxint) + m->machno; + else + enable = (u32int*)(LOCALREGS + Localtimerint) + m->machno; + v->mask = 1 << (irq - IRQlocal); + v->cpu = m->machno; + }else if(irq >= IRQbasic){ enable = &ip->ARMenable; v->reg = &ip->ARMpending; v->mask = 1 << (irq - IRQbasic); @@ -173,6 +246,7 @@ } v->f = f; v->a = a; + lock(&vctllock); if(irq == IRQfiq){ assert((ip->FIQctl & Fiqenable) == 0); assert((*enable & v->mask) == 0); @@ -181,8 +255,15 @@ }else{ v->next = vctl; vctl = v; - *enable = v->mask; + if(irq >= IRQmbox0){ + if(irq <= IRQmbox3) + *enable |= 1 << (irq - IRQmbox0); + }else if(irq >= IRQlocal) + *enable |= 1 << (irq - IRQlocal); + else + *enable = v->mask; } + unlock(&vctllock); } static char * @@ -226,8 +307,8 @@ char buf[ERRMAX]; if(up == nil) { - dumpregs(ureg); - panic("fault: nil up in faultarm, accessing %#p", va); + //dumpregs(ureg); + panic("fault: nil up in faultarm, pc %#p accessing %#p", ureg->pc, va); } insyscall = up->insyscall; up->insyscall = 1; @@ -308,8 +389,8 @@ 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); + panic("unknown trap; type %#lux, psr mode %#lux pc %lux", ureg->type, + ureg->psr & PsrMask, ureg->pc); break; case PsrMirq: clockintr = irq(ureg); @@ -524,13 +605,21 @@ * Fill in enough of Ureg to get a stack trace, and call a function. * Used by debugging interface rdb. */ + +static void +getpcsp(ulong *pc, ulong *sp) +{ + *pc = getcallerpc(&pc); + *sp = (ulong)&pc-4; +} + void callwithureg(void (*fn)(Ureg*)) { Ureg ureg; - ureg.pc = getcallerpc(&fn); - ureg.sp = PTR2UINT(&fn); + getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp); + ureg.r14 = getcallerpc(&fn); fn(&ureg); } diff -Nru /n/sources/plan9/sys/src/9/bcm/uartmini.c /sys/src/9/bcm/uartmini.c --- /n/sources/plan9/sys/src/9/bcm/uartmini.c Tue Jan 29 22:07:14 2013 +++ /sys/src/9/bcm/uartmini.c Tue May 31 00:00:00 2016 @@ -10,35 +10,11 @@ #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, @@ -76,54 +52,6 @@ .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<freq/(115200*8) - 1; if(ie){ intrenable(IRQaux, interrupt, uart, 0, "uart"); ap[MuIer] = RxIen|TxIen; @@ -417,8 +345,26 @@ okay(int on) { static int first; + static int okled, polarity; + char *p; - if(!first++) - gpiosel(OkLed, Output); - gpioout(OkLed, !on); + if(!first++){ + p = getconf("bcm2709.disk_led_gpio"); + if(p == nil) + p = getconf("bcm2708.disk_led_gpio"); + if(p != nil) + okled = strtol(p, 0, 0); + else + okled = 'v'; + p = getconf("bcm2709.disk_led_active_low"); + if(p == nil) + p = getconf("bcm2708.disk_led_active_low"); + polarity = (p == nil || *p == '1'); + if(okled != 'v') + gpiosel(okled, Output); + } + if(okled == 'v') + vgpset(0, on); + else if(okled != 0) + gpioout(okled, on^polarity); } diff -Nru /n/sources/plan9/sys/src/9/bcm/usbdwc.c /sys/src/9/bcm/usbdwc.c --- /n/sources/plan9/sys/src/9/bcm/usbdwc.c Fri Jun 28 21:34:09 2013 +++ /sys/src/9/bcm/usbdwc.c Tue May 31 00:00:00 2016 @@ -39,6 +39,7 @@ typedef struct Epio Epio; struct Ctlr { + Lock; Dwcregs *regs; /* controller registers */ int nchan; /* number of host channels */ ulong chanbusy; /* bitmap of in-use channels */ @@ -65,6 +66,22 @@ static void clog(Ep *ep, Hostchan *hc); static void logdump(Ep *ep); +static void +filock(Lock *l) +{ + int x; + + x = splfhi(); + ilock(l); + l->sr = x; +} + +static void +fiunlock(Lock *l) +{ + iunlock(l); +} + static Hostchan* chanalloc(Ep *ep) { @@ -157,23 +174,22 @@ Dwcregs *r; r = a; - return r->gintsts & Sofintr; + return (r->gintmsk & Sofintr) == 0; } static void sofwait(Ctlr *ctlr, int n) { Dwcregs *r; - int x; r = ctlr->regs; do{ + filock(ctlr); r->gintsts = Sofintr; - x = splfhi(); ctlr->sofchan |= 1<gintmsk |= Sofintr; + fiunlock(ctlr); sleep(&ctlr->chanintr[n], sofdone, r); - splx(x); }while((r->hfnum & 7) == 6); } @@ -191,7 +207,7 @@ static int chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask) { - int intr, n, x, ointr; + int intr, n, ointr; ulong start, now; Dwcregs *r; @@ -199,13 +215,14 @@ n = hc - r->hchan; for(;;){ restart: - x = splfhi(); + filock(ctlr); r->haintmsk |= 1<hcintmsk = mask; - sleep(&ctlr->chanintr[n], chandone, hc); + fiunlock(ctlr); + tsleep(&ctlr->chanintr[n], chandone, hc, 1000); + if((intr = hc->hcint) == 0) + goto restart; hc->hcintmsk = 0; - splx(x); - intr = hc->hcint; if(intr & Chhltd) return intr; start = fastticks(0); @@ -217,13 +234,14 @@ if((ointr != Ack && ointr != (Ack|Xfercomp)) || intr != (Ack|Chhltd|Xfercomp) || (now - start) > 60) - dprint("await %x after %ld %x -> %x\n", + dprint("await %x after %ldµs %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); + if(intr != Nak) + dprint("ep%d.%d await %x after %ldµs intr %x -> %x\n", + ep->dev->nb, ep->nb, mask, now - start, ointr, intr); goto restart; } now = fastticks(0); @@ -253,6 +271,8 @@ int i; hc = &ctlr->regs->hchan[n]; + if((hc->hcint & hc->hcintmsk) == 0) + return 1; if(ctlr->debugchan & (1<hcsplt & Spltena) == 0) @@ -346,7 +366,7 @@ else n = len; hc->hctsiz = n | npkt<hcdma = PADDR(a); + hc->hcdma = dmaaddr(a); nleft = len; logstart(ep); @@ -377,13 +397,19 @@ } hc->hcchar = (hc->hcchar &~ Chdis) | Chen; clog(ep, hc); +wait: if(ep->ttype == Tbulk && dir == Epin) - i = chanwait(ep, ctlr, hc, /* Ack| */ Chhltd); + i = chanwait(ep, ctlr, hc, 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); + if(hc->hcint != i){ + dprint("chanwait intr %ux->%ux\n", i, hc->hcint); + if((i = hc->hcint) == 0) + goto wait; + } hc->hcint = i; if(hc->hcsplt & Spltena){ @@ -409,7 +435,7 @@ if(i & ~(Chhltd|Ack)) error(Eio); if(hc->hcdma != hcdma) - print("usbotg: weird hcdma %x->%x intr %x->%x\n", + print("usbotg: weird hcdma %ux->%ux intr %ux->%ux\n", hcdma, hc->hcdma, i, hc->hcint); } n = hc->hcdma - hcdma; @@ -419,7 +445,7 @@ else continue; } - if(dir == Epin && ep->ttype == Tbulk && n == nleft){ + if(dir == Epin && ep->ttype == Tbulk){ nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize); if(nt != n){ if(n == ROUND(nt, 4)) @@ -523,8 +549,8 @@ 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); + epio->cb = b = allocb(ROUND(datalen, ep->maxpkt)); + assert(((uintptr)b->wp & (BLOCKALIGN-1)) == 0); memset(b->wp, 0x55, b->lim - b->wp); cachedwbinvse(b->wp, b->lim - b->wp); data = b->wp; @@ -549,6 +575,7 @@ }else b->wp += chanio(ep, hc, Epin, DATA1, data, datalen); chanio(ep, hc, Epout, DATA1, nil, 0); + cachedinvse(b->rp, BLEN(b)); n = Rsetuplen; }else{ if(datalen > 0) @@ -653,6 +680,7 @@ ctlr = hp->aux; r = ctlr->regs; wakechan = 0; + filock(ctlr); intr = r->gintsts; if(intr & Hcintr){ haint = r->haint & r->haintmsk; @@ -678,6 +706,7 @@ ctlr->wakechan |= wakechan; armtimerset(1); } + fiunlock(ctlr); } static void @@ -685,14 +714,14 @@ { Ctlr *ctlr; uint wakechan; - int i, x; + int i; ctlr = a; - x = splfhi(); + filock(ctlr); armtimerset(0); wakechan = ctlr->wakechan; ctlr->wakechan = 0; - splx(x); + fiunlock(ctlr); for(i = 0; wakechan; i++){ if(wakechan & 1) wakeup(&ctlr->chanintr[i]); @@ -772,10 +801,12 @@ /* fall through */ case Tbulk: /* XXX cache madness */ - b = allocb(ROUND(n, ep->maxpkt) + CACHELINESZ); - p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ); - cachedwbinvse(p, n); + b = allocb(ROUND(n, ep->maxpkt)); + p = b->rp; + assert(((uintptr)p & (BLOCKALIGN-1)) == 0); + cachedinvse(p, n); nr = eptrans(ep, Read, p, n); + cachedinvse(p, nr); epio->lastpoll = TK2MS(m->ticks); memmove(a, p, nr); qunlock(epio); @@ -814,8 +845,9 @@ case Tctl: case Tbulk: /* XXX cache madness */ - b = allocb(n + CACHELINESZ); - p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ); + b = allocb(n); + p = b->wp; + assert(((uintptr)p & (BLOCKALIGN-1)) == 0); memmove(p, a, n); cachedwbse(p, n); if(ep->ttype == Tctl) diff -Nru /n/sources/plan9/sys/src/9/bcm/vcore.c /sys/src/9/bcm/vcore.c --- /n/sources/plan9/sys/src/9/bcm/vcore.c Tue Jan 29 22:07:04 2013 +++ /sys/src/9/bcm/vcore.c Tue May 31 00:00:00 2016 @@ -12,6 +12,7 @@ typedef struct Prophdr Prophdr; typedef struct Fbinfo Fbinfo; +typedef struct Vgpio Vgpio; enum { Read = 0x00>>2, @@ -33,12 +34,16 @@ TagResp = 1<<31, TagGetfwrev = 0x00000001, + TagGetrev = 0x00010002, TagGetmac = 0x00010003, TagGetram = 0x00010005, TagGetpower = 0x00020001, TagSetpower = 0x00028001, Powerwait = 1<<1, TagGetclkspd= 0x00030002, + TagGetclkmax= 0x00030004, + TagSetclkspd= 0x00038002, + TagGettemp = 0x00030006, TagFballoc = 0x00040001, TagFbfree = 0x00048001, TagFbblank = 0x00040002, @@ -48,8 +53,11 @@ TagSetvres = 0x00048004, TagGetdepth = 0x00040005, TagSetdepth = 0x00048005, - TagGetrgb = 0x00044006, + TagGetrgb = 0x00040006, TagSetrgb = 0x00048006, + TagGetGpio = 0x00040010, + + Nvgpio = 2, }; struct Fbinfo { @@ -75,6 +83,15 @@ u32int data[1]; }; +struct Vgpio { + u32int *counts; + u16int incs; + u16int decs; + int ison; +}; + +static Vgpio vgpio; + static void vcwrite(uint chan, int val) { @@ -114,7 +131,8 @@ uintptr r; int n; Prophdr *prop; - static uintptr base = BUSDRAM; + uintptr aprop; + static int busaddr = 1; if(rsplen < vallen) rsplen = vallen; @@ -131,13 +149,14 @@ memmove(prop->data, buf, vallen); cachedwbinvse(prop, prop->len); for(;;){ - vcwrite(ChanProps, PADDR(prop) + base); + aprop = busaddr? dmaaddr(prop) : PTR2UINT(prop); + vcwrite(ChanProps, aprop); r = vcread(ChanProps); - if(r == PADDR(prop) + base) + if(r == aprop) break; - if(base == 0) + if(!busaddr) return -1; - base = 0; + busaddr = 0; } if(prop->req == RspOk && prop->tag == tag && @@ -159,13 +178,17 @@ fbdefault(int *width, int *height, int *depth) { u32int buf[3]; + char *p; 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]; + if((p = getconf("bcm2708_fb.fbdepth")) != nil) + *depth = atoi(p); + else + *depth = buf[2]; return 0; } @@ -185,7 +208,7 @@ fi->yres = fi->yresvirtual = *height; fi->bpp = *depth; cachedwbinvse(fi, sizeof(*fi)); - vcwrite(ChanFb, DMAADDR(fi)); + vcwrite(ChanFb, dmaaddr(fi)); if(vcread(ChanFb) != 0) return 0; va = mmukmap(FRAMEBUFFER, PADDR(fi->base), fi->screensize); @@ -251,6 +274,19 @@ } /* + * Get board revision + */ +uint +getboardrev(void) +{ + u32int buf[1]; + + if(vcreq(TagGetrev, buf, 0, sizeof buf) != sizeof buf) + return 0; + return buf[0]; +} + +/* * Get firmware revision */ uint @@ -289,4 +325,65 @@ if(vcreq(TagGetclkspd, buf, sizeof(buf[0]), sizeof(buf)) != sizeof buf) return 0; return buf[1]; +} + +/* + * Set clock rate to hz (or max speed if hz == 0) + */ +void +setclkrate(int clkid, ulong hz) +{ + u32int buf[2]; + + buf[0] = clkid; + if(hz != 0) + buf[1] = hz; + else if(vcreq(TagGetclkmax, buf, sizeof(buf[0]), sizeof(buf)) != sizeof buf) + return; + vcreq(TagSetclkspd, buf, sizeof(buf), sizeof(buf)); +} + +/* + * Get cpu temperature + */ +uint +getcputemp(void) +{ + u32int buf[2]; + + buf[0] = 0; + if(vcreq(TagGettemp, buf, sizeof(buf[0]), sizeof buf) != sizeof buf) + return 0; + return buf[1]; +} + +/* + * Virtual GPIO - used for ACT LED on pi3 + */ +void +vgpinit(void) +{ + u32int buf[1]; + uintptr va; + + buf[0] = 0; + if(vcreq(TagGetGpio, buf, 0, sizeof(buf)) != sizeof buf || buf[0] == 0) + return; + va = mmukmap(VGPIO, buf[0] & ~0xC0000000, BY2PG); + if(va == 0) + return; + vgpio.counts = (u32int*)va; +} + +void +vgpset(uint port, int on) +{ + if(vgpio.counts == nil || port >= Nvgpio || on == vgpio.ison) + return; + if(on) + vgpio.incs++; + else + vgpio.decs++; + vgpio.counts[port] = (vgpio.incs << 16) | vgpio.decs; + vgpio.ison = on; } diff -Nru /n/sources/plan9/sys/src/9/bcm/vfp3.c /sys/src/9/bcm/vfp3.c --- /n/sources/plan9/sys/src/9/bcm/vfp3.c Tue Jan 29 22:06:54 2013 +++ /sys/src/9/bcm/vfp3.c Tue May 31 00:00:00 2016 @@ -1 +1,518 @@ -#include "../teg2/vfp3.c" +/* + * 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 | FPRNR | (FPINVAL | FPZDIV | FPOVFL) & ~Alltraps; + /* VFPv2 needs software support for underflows, so force them to zero */ + if(m->havefp == VFPv2) + m->fpscr |= Fz; + 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) + fpoff(); + 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 *) +{ + 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("sys: 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; + static int already; + + 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); + 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? */ + s = spllo(); + if(!already++) + pprint("warning: emulated arm7500 fpa instr %#8.8lux at %#p\n", *(ulong *)pc, pc); + 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 off or unsupported instruction */ + mathemu(ureg); /* enable fpu & retry */ + nfp = 1; + } + + poperror(); + return nfp; +} diff -Nru /n/sources/plan9/sys/src/9/bcm/words.pi2 /sys/src/9/bcm/words.pi2 --- /n/sources/plan9/sys/src/9/bcm/words.pi2 Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/words.pi2 Tue May 31 00:00:00 2016 @@ -0,0 +1,11 @@ +raspberry pi 2 -- differences from raspberry pi + +broadcom 2836 SoC (based on 2709) +4 x cortex-a7 (v7 arch) 900Mhz cpu, dual-issue, vfpv3+ + +integral l2 cache + +1GB of dram at physical address 0 + +peripherals at 0x3F000000 +gpu/dma space at 0xC0000000