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 Sun Apr 11 00:00:00 2021 @@ -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 Sun Apr 11 00:00:00 2021 @@ -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,63 @@ 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 +ainc(long *p) +{ + int s, v; + + s = splhi(); + v = ++*p; + splx(s); + return v; +} + +long +adec(long *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 Sun Apr 11 00:00:00 2021 @@ -0,0 +1,280 @@ +/* + * 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, + .oscfreq = 19200000, + .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; + case 0xd08: + p = seprint(buf, buf + size, "Cortex-A72"); + 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); + coherence(); + sev(); + 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, timeout; + + 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; + timeout = 10000000; + while(!canlock(&startlock[i])) + if(--timeout == 0) + return i; + unlock(&startlock[i]); + } + return ncpu; +} + +void +archbcm2link(void) +{ + addclock0link(wdogfeed, HZ); +} + +int +archether(unsigned ctlrno, Ether *ether) +{ + switch(ctlrno){ + case 0: + ether->type = "usb"; + break; + case 1: + ether->type = "4330"; + break; + default: + return 0; + } + ether->ctlrno = ctlrno; + ether->irq = -1; + ether->nopt = 0; + ether->maxmtu = 9014; + 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/archbcm4.c /sys/src/9/bcm/archbcm4.c --- /n/sources/plan9/sys/src/9/bcm/archbcm4.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/archbcm4.c Sun Apr 11 00:00:00 2021 @@ -0,0 +1,280 @@ +/* + * bcm2711 (raspberry pi 4) 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 = 0xFC000000, + .physio = 0xFE000000, + .busdram = 0xC0000000, + .busio = 0x7E000000, + .armlocal = 0xFF800000, + .oscfreq = 54000000, + .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; + case 0xd08: + p = seprint(buf, buf + size, "Cortex-A72"); + 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); + coherence(); + sev(); + 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, timeout; + + 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; + timeout = 10000000; + while(!canlock(&startlock[i])) + if(--timeout == 0) + return i; + unlock(&startlock[i]); + } + return ncpu; +} + +void +archbcm4link(void) +{ + addclock0link(wdogfeed, HZ); +} + +int +archether(unsigned ctlrno, Ether *ether) +{ + switch(ctlrno){ + case 0: + ether->type = "genet"; + break; + case 1: + ether->type = "4330"; + break; + default: + return 0; + } + ether->ctlrno = ctlrno; + ether->irq = -1; + ether->nopt = 0; + ether->maxmtu = 9014; + 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 Sun Apr 11 00:00:00 2021 @@ -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. */ @@ -248,6 +280,7 @@ #define Coarse (Mbz|1) /* L1 */ #define Section (Mbz|2) /* L1 1MB */ #define Fine (Mbz|3) /* L1 */ +#define Super (1<<18) /* L1 16MB */ #define Large 0x00000001 /* L2 64KB */ #define Small 0x00000002 /* L2 4KB */ @@ -256,6 +289,16 @@ #define Cached 0x00000008 /* L[12] */ #define Dom0 0 +#define L1wralloc (1<<12) /* L1 TEX */ +#define L1sharable (1<<16) +#define L1noexec (1<<4) +#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 +310,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 Sun Apr 11 00:00:00 2021 @@ -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 Sun Apr 11 00:00:00 2021 @@ -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 Sun Apr 11 00:00:00 2021 @@ -0,0 +1,492 @@ +/* + * 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 */ +#define SEV WORD $0xe320f004 /* send event */ + +/* 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 + BIC $KSEGM, 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 + +TEXT sev(SB), $-4 + SEV + 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/bootwifi.rc /sys/src/9/bcm/bootwifi.rc --- /n/sources/plan9/sys/src/9/bcm/bootwifi.rc Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/bootwifi.rc Sun Apr 11 00:00:00 2021 @@ -0,0 +1,70 @@ +#!/boot/rc -m /boot/rcmain + +wifi=/net/ether1 + +fn need { + while (~ $#$1 0) { + echo -n $1': ' + $1=`{read} + } +} + +fn joinwifi { + need essid + echo essid $essid >$wifi/clone + need wificrypt + wifip=p + if (! ~ $#wifipass 0) { + factotum -g 'proto=wpapsk essid='$essid' !password='$wifipass + wifip='' + } + switch ($wificrypt) { + case wep + echo crypt wep >$wifi/clone + need wep_password + echo key0 $wep_password >$wifi/clone + rm /env/wep_password + case wpa wpa1 + wpa -1p $wifi + case wpa2 + wpa -2$wifip $wifi + } +} + +path=(/bin /boot) +bind '#p' /proc +bind '#d' /fd +bind -a '#P' /dev +bind -a '#t' /dev +bind -a '#S' /dev +bind -a '#I' /net +bind -a '#l0' /net +bind -a '#l1' /net || wifi=/net/ether0 + +usbd + +need fs +need auth + +factotum -u -s factotum -a $auth +joinwifi +ipconfig ether $wifi +srv tcp!$fs!564 boot + +rootdir=/root +rootspec='' +mount -c /srv/boot /root +bind -ac $rootdir / +bind -ac $rootdir/mnt /mnt + +bind /$cputype/bin /bin +bind -a /rc/bin /bin +path=(. /bin) + +if (! ~ $#init 0) + exec `{echo $init} +if (~ $service cpu) + exec /$cputype/init -c +if not + exec /$cputype/init -t +exec /boot/rc -m/boot/rcmain -i 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 Sun Apr 11 00:00:00 2021 @@ -0,0 +1,221 @@ +/* + * 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 + DSB + 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 Sun Apr 11 00:00:00 2021 @@ -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,24 @@ { Systimers *tn; + if(m->machno != 0) + panic("cpu%d: unexpected system timer interrupt", m->machno); tn = (Systimers*)SYSTIMERS; /* dismiss interrupt */ + tn->c3 = tn->clo - 1; tn->cs = 1<<3; timerintr(ureg, 0); } +static void +localclockintr(Ureg *ureg, void *) +{ + if(m->machno == 0) + panic("cpu0: Unexpected local generic timer interrupt"); + cpwrtimerphysctl(Imask|Enable); + timerintr(ureg, 0); +} + void clockshutdown(void) { @@ -84,7 +110,10 @@ tm = (Armtimer*)ARMTIMER; tm->ctl = 0; - wdogoff(); + if(cpuserver) + wdogfeed(); + else + wdogoff(); } void @@ -94,11 +123,18 @@ Armtimer *tm; u32int t0, t1, tstart, tend; - tn = (Systimers*)SYSTIMERS; - tm = (Armtimer*)ARMTIMER; - tm->load = 0; - tm->ctl = TmrPrescale1|CntEnable|CntWidth32; + if(((cprdfeat1() >> 16) & 0xF) != 0) { + /* generic timer supported */ + if(m->machno == 0){ + /* input clock is 19.2MHz or 54MHz crystal */ + *(ulong*)(ARMLOCAL + Localctl) = 0; + /* divide by (2^31/Prescaler) for 1Mhz */ + *(ulong*)(ARMLOCAL + Prescaler) = (((uvlong)SystimerFreq<<31)/soc.oscfreq)&~1UL; + } + cpwrtimerphysctl(Imask); + } + tn = (Systimers*)SYSTIMERS; tstart = tn->clo; do{ t0 = lcycles(); @@ -111,25 +147,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){ + cpwrtimerphysval(period); + cpwrtimerphysctl(Enable); + }else{ + tn = (Systimers*)SYSTIMERS; + tn->c3 = tn->clo + period; + } } uvlong @@ -137,16 +184,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 +227,7 @@ { if(SystimerFreq != 1*Mhz) return fastticks2us(fastticks(nil)); - return fastticks(nil); + return ((Systimers*)SYSTIMERS)->clo; } void @@ -188,8 +236,8 @@ Systimers *tn; u32int now, diff; - tn = (Systimers*)SYSTIMERS; diff = n + 1; + tn = (Systimers*)SYSTIMERS; now = tn->clo; while(tn->clo - now < diff) ; Binary files /n/sources/plan9/sys/src/9/bcm/cmd-usb.tar and /sys/src/9/bcm/cmd-usb.tar differ 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 Sun Apr 11 00:00:00 2021 @@ -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; @@ -105,7 +107,7 @@ { ulong nmach; /* processors */ ulong nproc; /* processes */ - Confmem mem[1]; /* physical memory */ + Confmem mem[2]; /* physical memory */ ulong npage; /* total physical pages of memory */ usize upages; /* user page pool */ ulong copymode; /* 0 is copy on write, 1 is copy on reference */ @@ -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 */ @@ -135,16 +159,20 @@ int mmul1lo; int mmul1hi; int mmupid; + PTE* kmapl2; /* l2 for section containing kmap area and vectors */ }; /* * MMU stuff in proc */ #define NCOLOR 1 /* 1 level cache, don't worry about VCE's */ +#define NKMAPS 4 struct PMMU { Page* mmul2; Page* mmul2cache; /* free mmu pages */ + int nkmap; + PTE kmaptab[NKMAPS]; }; #include "../port/portdat.h" @@ -216,8 +244,9 @@ */ typedef void KMap; #define VA(k) ((uintptr)(k)) -#define kmap(p) (KMap*)((p)->pa|kseg0) -#define kunmap(k) +//#define kmap(p) (KMap*)((p)->pa|kseg0) +extern KMap* kmap(Page*); +extern void kunmap(KMap*); struct { @@ -284,3 +313,14 @@ Devport *ports; /* The ports themselves */ }; +struct Soc { /* SoC dependent configuration */ + ulong dramsize; + uintptr physio; + uintptr busdram; + uintptr busio; + uintptr armlocal; + uint oscfreq; + 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 Sun Apr 11 00:00:00 2021 @@ -150,9 +150,30 @@ 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); +} + +extern uvlong getserial(void); + +static long +cpuserread(Chan*, void *a, long n, vlong offset) +{ + char str[16]; + + snprint(str, sizeof str, "%16.16llux", getserial()); return readstr(offset, a, n, str); } @@ -160,4 +181,6 @@ archinit(void) { addarchfile("cputype", 0444, cputyperead, nil); + addarchfile("cputemp", 0444, cputempread, nil); + addarchfile("serial", 0444, cpuserread, nil)->length = 16; } 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 Sun Apr 11 00:00:00 2021 @@ -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 = VIRTIO + 0x200034, +}; + +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 Sun Apr 11 00:00:00 2021 @@ -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 Sun Apr 11 00:00:00 2021 @@ -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 Sun Apr 11 00:00:00 2021 @@ -0,0 +1,234 @@ +/* + * 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" + +#define SPIREGS (VIRTIO+0x204000) + +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 *)SPIREGS; + 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/devusb.c /sys/src/9/bcm/devusb.c --- /n/sources/plan9/sys/src/9/bcm/devusb.c Fri Jun 28 21:34:09 2013 +++ /sys/src/9/bcm/devusb.c Sun Apr 11 00:00:00 2021 @@ -45,7 +45,7 @@ #include "fns.h" #include "io.h" #include "../port/error.h" -#include "../port/usb.h" +#include "usb.h" typedef struct Hcitype Hcitype; @@ -73,7 +73,7 @@ /* Ep. ctls */ CMnew = 0, /* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */ - CMnewdev, /* newdev full|low|high portnb (allocate new devices) */ + CMnewdev, /* newdev full|low|high|super portnb (allocate new devices) */ CMhub, /* hub (set the device as a hub) */ CMspeed, /* speed full|low|high|no */ CMmaxpkt, /* maxpkt size */ @@ -154,6 +154,7 @@ static char *spname[] = { + [Superspeed] "super", [Fullspeed] "full", [Lowspeed] "low", [Highspeed] "high", @@ -289,6 +290,8 @@ s = seprint(s, se, " hz %ld", ep->hz); s = seprint(s, se, " hub %d", ep->dev->hub); s = seprint(s, se, " port %d", ep->dev->port); + s = seprint(s, se, " rootport %d", ep->dev->rootport); + s = seprint(s, se, " addr %d", ep->dev->addr); if(ep->inuse) s = seprint(s, se, " busy"); else @@ -452,15 +455,16 @@ ep = epalloc(hp); d = ep->dev = smalloc(sizeof(Udev)); d->nb = newusbid(hp); + d->addr = 0; d->eps[0] = ep; ep->nb = 0; ep->toggle[0] = ep->toggle[1] = 0; d->ishub = ishub; d->isroot = isroot; - if(hp->highspeed != 0) - d->speed = Highspeed; - else - d->speed = Fullspeed; + d->rootport = 0; + d->routestr = 0; + d->depth = -1; + d->speed = Fullspeed; d->state = Dconfig; /* address not yet set */ ep->dev = d; ep->ep0 = ep; /* no ref counted here */ @@ -721,6 +725,17 @@ print("usbreset: bug: Nhcis too small\n"); } +static int +numbits(uint n) +{ + int c = 0; + while(n != 0){ + c++; + n = (n-1) & n; + } + return c; +} + static void usbinit(void) { @@ -733,13 +748,37 @@ for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){ hp = hcis[ctlrno]; if(hp != nil){ - if(hp->init != nil) + int n; + + if(hp->init != nil){ + if(waserror()){ + print("usbinit: %s: %s\n", hp->type, up->errstr); + continue; + } hp->init(hp); - d = newdev(hp, 1, 1); /* new root hub */ - d->dev->state = Denabled; /* although addr == 0 */ - d->maxpkt = 64; - snprint(info, sizeof(info), "ports %d", hp->nports); - kstrdup(&d->info, info); + poperror(); + } + + hp->superspeed &= (1<nports)-1; + n = hp->nports - numbits(hp->superspeed); + if(n > 0){ + d = newdev(hp, 1, 1); /* new LS/FS/HS root hub */ + d->maxpkt = 64; + if(hp->highspeed != 0) + d->dev->speed = Highspeed; + d->dev->state = Denabled; /* although addr == 0 */ + snprint(info, sizeof(info), "roothub ports %d", n); + kstrdup(&d->info, info); + } + n = numbits(hp->superspeed); + if(n > 0){ + d = newdev(hp, 1, 1); /* new SS root hub */ + d->maxpkt = 512; + d->dev->speed = Superspeed; + d->dev->state = Denabled; /* although addr == 0 */ + snprint(info, sizeof(info), "roothub ports %d", n); + kstrdup(&d->info, info); + } } } } @@ -838,7 +877,7 @@ error(Enotconf); ep->clrhalt = 0; ep->rhrepl = -1; - if(ep->load == 0) + if(ep->load == 0 && ep->dev->speed != Superspeed) ep->load = usbload(ep->dev->speed, ep->maxpkt); ep->hp->epopen(ep); @@ -960,20 +999,57 @@ static long rhubread(Ep *ep, void *a, long n) { - char *b; + uchar b[8]; - if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2) - return -1; - if(ep->rhrepl < 0) + if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2 || ep->rhrepl == -1) return -1; - b = a; - memset(b, 0, n); - PUT2(b, ep->rhrepl); + b[0] = ep->rhrepl; + b[1] = ep->rhrepl>>8; + b[2] = ep->rhrepl>>16; + b[3] = ep->rhrepl>>24; + b[4] = ep->rhrepl>>32; + b[5] = ep->rhrepl>>40; + b[6] = ep->rhrepl>>48; + b[7] = ep->rhrepl>>56; + ep->rhrepl = -1; + + if(n > sizeof(b)) + n = sizeof(b); + memmove(a, b, n); + return n; } +static int +rootport(Ep *ep, int port) +{ + Hci *hp; + Udev *hub; + uint mask; + int rootport; + + hp = ep->hp; + hub = ep->dev; + if(!hub->isroot) + return hub->rootport; + + mask = hp->superspeed; + if(hub->speed != Superspeed) + mask = (1<nports)-1 & ~mask; + + for(rootport = 1; mask != 0; rootport++){ + if(mask & 1){ + if(--port == 0) + return rootport; + } + mask >>= 1; + } + + return 0; +} + static long rhubwrite(Ep *ep, void *a, long n) { @@ -994,8 +1070,8 @@ hp = ep->hp; cmd = s[Rreq]; feature = GET2(s+Rvalue); - port = GET2(s+Rindex); - if(port < 1 || port > hp->nports) + port = rootport(ep, GET2(s+Rindex)); + if(port == 0) error("bad hub port number"); switch(feature){ case Rportenable: @@ -1068,20 +1144,34 @@ static void setmaxpkt(Ep *ep, char* s) { - long spp; /* samples per packet */ + long spp, max; /* samples per packet */ - if(ep->dev->speed == Highspeed) - spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000; - else + if(ep->dev->speed == Fullspeed) spp = (ep->hz * ep->pollival + 999) / 1000; + else + spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000; ep->maxpkt = spp * ep->samplesz; deprint("usb: %s: setmaxpkt: hz %ld poll %ld" " ntds %d %s speed -> spp %ld maxpkt %ld\n", s, ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed], spp, ep->maxpkt); - if(ep->maxpkt > 1024){ - print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt); - ep->maxpkt = 1024; + switch(ep->dev->speed){ + case Fullspeed: + max = 1024; + break; + case Highspeed: + max = 3*1024; + break; + case Superspeed: + max = 48*1024; + break; + default: + return; + } + if(ep->maxpkt*ep->ntds > max){ + print("usb: %s: maxpkt %ld > %ld for %s, truncating\n", + s, ep->maxpkt*ep->ntds, max, spname[ep->dev->speed]); + ep->maxpkt = max/ep->ntds; } } @@ -1138,13 +1228,20 @@ error("not a hub setup endpoint"); l = name2speed(cb->f[1]); if(l == Nospeed) - error("speed must be full|low|high"); + error("speed must be full|low|high|super"); + if(l != d->speed && (l == Superspeed || d->speed == Superspeed)) + error("wrong speed for superspeed hub/device"); nep = newdev(ep->hp, 0, 0); nep->dev->speed = l; - if(nep->dev->speed != Lowspeed) + if(l == Superspeed) + nep->maxpkt = 512; + else if(l != Lowspeed) nep->maxpkt = 64; /* assume full speed */ - nep->dev->hub = d->nb; + nep->dev->hub = d->addr; nep->dev->port = atoi(cb->f[2]); + nep->dev->depth = d->depth+1; + nep->dev->rootport = rootport(ep, nep->dev->port); + nep->dev->routestr = d->routestr | (((nep->dev->port&15) << 4*nep->dev->depth) >> 4); /* next read request will read * the name for the new endpoint */ @@ -1160,7 +1257,9 @@ l = name2speed(cb->f[1]); deprint("usb epctl %s %d\n", cb->f[0], l); if(l == Nospeed) - error("speed must be full|low|high"); + error("speed must be full|low|high|super"); + if(l != d->speed && (l == Superspeed || d->speed == Superspeed)) + error("cannot change speed on superspeed device"); qlock(ep->ep0); d->speed = l; qunlock(ep->ep0); @@ -1252,6 +1351,8 @@ break; case CMaddress: deprint("usb epctl %s\n", cb->f[0]); + if(ep->dev->addr == 0) + ep->dev->addr = ep->dev->nb; ep->dev->state = Denabled; break; case CMdetach: 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 Sun Apr 11 00:00:00 2021 @@ -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,20 @@ static Ctlr dma[Nchan]; static u32int *dmaregs = (u32int*)DMAREGS; +uintptr +dmaaddr(void *va) +{ + if(PTR2UINT(va)&0x40000000) + panic("dma address %#p (from%#p)\n", va, getcallerpc(&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) { @@ -158,21 +172,21 @@ case DmaD2M: cachedwbinvse(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); DBG print("intstatus %ux\n", dmaregs[Intstatus]); dmaregs[Intstatus] = 0; ctlr->regs[Cs] = Int; - microdelay(1); coherence(); + microdelay(1); DBG dumpdregs("before Active", ctlr->regs); ctlr->regs[Cs] = Active; DBG dumpdregs("after Active", ctlr->regs); 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 Sun Apr 11 00:00:00 2021 @@ -4,6 +4,14 @@ * Copyright © 2012 Richard Miller */ +/* + Not officially documented: emmc can be connected to different gpio pins + 48-53 (SD card) + 22-27 (P1 header) + 34-39 (wifi - pi3 only) + using ALT3 function to activate the required routing + */ + #include "u.h" #include "../port/lib.h" #include "../port/error.h" @@ -20,10 +28,17 @@ /* not available from vcore */ Initfreq = 400000, /* initialisation frequency for MMC */ SDfreq = 25*Mhz, /* standard SD frequency */ + SDfreqhs = 50*Mhz, /* high speed frequency */ DTO = 14, /* data timeout exponent (guesswork) */ + GoIdle = 0, /* mmc/sdio go idle state */ MMCSelect = 7, /* mmc/sd card select command */ Setbuswidth = 6, /* mmc/sd set bus width command */ + Switchfunc = 6, /* mmc/sd switch function command */ + Voltageswitch = 11, /* md/sdio switch to 1.8V */ + IORWdirect = 52, /* sdio read/write direct command */ + IORWextended = 53, /* sdio read/write extended command */ + Appcmd = 55, /* mmc/sd application command prefix */ }; enum { @@ -56,6 +71,7 @@ Slotisrver = 0xfc>>2, /* Control0 */ + Hispeed = 1<<2, Dwidth4 = 1<<1, Dwidth1 = 0<<1, @@ -105,7 +121,7 @@ Ccrcerr = 1<<17, Ctoerr = 1<<16, Err = 1<<15, - Cardintr = 1<<8, /* not in Broadcom datasheet */ + Cardintr = 1<<8, Cardinsert = 1<<6, /* not in Broadcom datasheet */ Readrdy = 1<<5, Writerdy = 1<<4, @@ -122,14 +138,16 @@ Cmdinhibit = 1<<0, }; -int cmdinfo[64] = { +static int cmdinfo[64] = { [0] Ixchken, [2] Resp136, [3] Resp48 | Ixchken | Crcchken, +[5] Resp48, [6] Resp48 | Ixchken | Crcchken, [7] Resp48busy | Ixchken | Crcchken, [8] Resp48 | Ixchken | Crcchken, [9] Resp136, +[11] Resp48 | Ixchken | Crcchken, [12] Resp48busy | Ixchken | Crcchken, [13] Resp48 | Ixchken | Crcchken, [16] Resp48, @@ -138,6 +156,8 @@ [24] Resp48 | Isdata | Host2card | Ixchken | Crcchken, [25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken, [41] Resp48, +[52] Resp48 | Ixchken | Crcchken, +[53] Resp48 | Ixchken | Crcchken | Isdata, [55] Resp48 | Ixchken | Crcchken, }; @@ -145,9 +165,10 @@ struct Ctlr { Rendez r; - int datadone; + Rendez cardr; int fastclock; ulong extclk; + int appcmd; }; static Ctlr emmc; @@ -161,6 +182,7 @@ if(0)print("WR %2.2ux %ux\n", reg<<2, val); microdelay(emmc.fastclock? 2 : 20); + coherence(); r[reg] = val; } @@ -175,10 +197,46 @@ return v; } +static void +emmcclk(uint freq) +{ + u32int *r; + uint div; + int i; + + r = (u32int*)EMMCREGS; + div = emmc.extclk / (freq<<1); + if(emmc.extclk / (div<<1) > freq) + div++; + WR(Control1, clkdiv(div) | + DTO<ticks-now > HZ) break; if((i&(Cmddone|Err)) != Cmddone){ - if((i&~Err) != Ctoerr) - print("emmc: cmd %ux error intr %ux stat %ux\n", c, i, r[Status]); + if((i&~(Err|Cardintr)) != Ctoerr) + print("emmc: cmd %ux arg %ux error intr %ux stat %ux\n", c, arg, i, r[Status]); WR(Interrupt, i); if(r[Status]&Cmdinhibit){ WR(Control1, r[Control1]|Srstcmd); @@ -308,11 +394,9 @@ break; } if((c & Respmask) == Resp48busy){ - WR(Irpten, Datadone|Err); + WR(Irpten, r[Irpten]|Datadone|Err); tsleep(&emmc.r, datadone, 0, 3000); - i = emmc.datadone; - emmc.datadone = 0; - WR(Irpten, 0); + i = r[Interrupt]; if((i & Datadone) == 0) print("emmcio: no Datadone after CMD%d\n", cmd); if(i & Err) @@ -324,30 +408,46 @@ * Once card is selected, use faster clock */ if(cmd == MMCSelect){ - delay(10); - WR(Control1, clkdiv(emmc.extclk/SDfreq - 1) | - DTO<> 8; + return p + 2; +} + +static uchar* +put4(uchar *p, long v) +{ + p[0] = v; + p[1] = v >> 8; + p[2] = v >> 16; + p[3] = v >> 24; + return p + 4; +} + +static ushort +get2(uchar *p) +{ + return p[0] | p[1]<<8; +} + +static ulong +get4(uchar *p) +{ + return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24; +} + +static void +dump(char *s, void *a, int n) +{ + int i; + uchar *p; + + p = a; + print("%s:", s); + for(i = 0; i < n; i++) + print("%c%2.2x", i&15? ' ' : '\n', *p++); + print("\n"); +} + +/* + * SDIO communication with dongle + */ +static ulong +sdiocmd_locked(int cmd, ulong arg) +{ + u32int resp[4]; + + sdio.cmd(cmd, arg, resp); + return resp[0]; +} + +static ulong +sdiocmd(int cmd, ulong arg) +{ + ulong r; + + qlock(&sdiolock); + if(waserror()){ + if(SDIODEBUG) print("sdiocmd error: cmd %d arg %lux\n", cmd, arg); + qunlock(&sdiolock); + nexterror(); + } + r = sdiocmd_locked(cmd, arg); + qunlock(&sdiolock); + poperror(); + return r; + +} + +static ulong +trysdiocmd(int cmd, ulong arg) +{ + ulong r; + + if(waserror()) + return 0; + r = sdiocmd(cmd, arg); + poperror(); + return r; +} + +static int +sdiord(int fn, int addr) +{ + int r; + + r = sdiocmd(IO_RW_DIRECT, (0<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9)); + if(r & 0xCF00){ + print("ether4330: sdiord(%x, %x) fail: %2.2ux %2.2ux\n", fn, addr, (r>>8)&0xFF, r&0xFF); + error(Eio); + } + return r & 0xFF; +} + +static void +sdiowr(int fn, int addr, int data) +{ + int r; + int retry; + + r = 0; + for(retry = 0; retry < 10; retry++){ + r = sdiocmd(IO_RW_DIRECT, (1<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9)|(data&0xFF)); + if((r & 0xCF00) == 0) + return; + } + print("ether4330: sdiowr(%x, %x, %x) fail: %2.2ux %2.2ux\n", fn, addr, data, (r>>8)&0xFF, r&0xFF); + error(Eio); +} + +static void +sdiorwext(int fn, int write, void *a, int len, int addr, int incr) +{ + int bsize, blk, bcount, m; + + bsize = fn == Fn2? 512 : 64; + while(len > 0){ + if(len >= 511*bsize){ + blk = 1; + bcount = 511; + m = bcount*bsize; + }else if(len > bsize){ + blk = 1; + bcount = len/bsize; + m = bcount*bsize; + }else{ + blk = 0; + bcount = len; + m = bcount; + } + qlock(&sdiolock); + if(waserror()){ + print("ether4330: sdiorwext fail: %s\n", up->errstr); + qunlock(&sdiolock); + nexterror(); + } + if(blk) + sdio.iosetup(write, a, bsize, bcount); + else + sdio.iosetup(write, a, bcount, 1); + sdiocmd_locked(IO_RW_EXTENDED, + write<<31 | (fn&7)<<28 | blk<<27 | incr<<26 | (addr&0x1FFFF)<<9 | (bcount&0x1FF)); + sdio.io(write, a, m); + qunlock(&sdiolock); + poperror(); + len -= m; + a = (char*)a + m; + if(incr) + addr += m; + } +} + +static void +sdioset(int fn, int addr, int bits) +{ + sdiowr(fn, addr, sdiord(fn, addr) | bits); +} + +static void +sdioinit(void) +{ + ulong ocr, rca; + int i; + + /* disconnect emmc from SD card (connect sdhost instead) */ + for(i = 48; i <= 53; i++) + gpiosel(i, Alt0); + /* connect emmc to wifi */ + for(i = 34; i <= 39; i++){ + gpiosel(i, Alt3); + if(i == 34) + gpiopulloff(i); + else + gpiopullup(i); + } + sdio.init(); + sdio.enable(); + sdiocmd(GO_IDLE_STATE, 0); + ocr = trysdiocmd(IO_SEND_OP_COND, 0); + i = 0; + while((ocr & (1<<31)) == 0){ + if(++i > 5){ + print("ether4330: no response to sdio access: ocr = %lux\n", ocr); + error(Eio); + } + ocr = trysdiocmd(IO_SEND_OP_COND, V3_3); + tsleep(&up->sleep, return0, nil, 100); + } + rca = sdiocmd(SEND_RELATIVE_ADDR, 0) >> Rcashift; + sdiocmd(SELECT_CARD, rca << Rcashift); + sdioset(Fn0, Highspeed, 2); + sdioset(Fn0, Busifc, 2); /* bus width 4 */ + sdiowr(Fn0, Fbr1+Blksize, 64); + sdiowr(Fn0, Fbr1+Blksize+1, 64>>8); + sdiowr(Fn0, Fbr2+Blksize, 512); + sdiowr(Fn0, Fbr2+Blksize+1, 512>>8); + sdioset(Fn0, Ioenable, 1<sleep, return0, nil, 100); + } +} + +static void +sdioreset(void) +{ + sdiowr(Fn0, Ioabort, 1<<3); /* reset */ +} + +static void +sdioabort(int fn) +{ + sdiowr(Fn0, Ioabort, fn); +} + +/* + * Chip register and memory access via SDIO + */ + +static void +cfgw(ulong off, int val) +{ + sdiowr(Fn1, off, val); +} + +static int +cfgr(ulong off) +{ + return sdiord(Fn1, off); +} + +static ulong +cfgreadl(int fn, ulong off) +{ + uchar cbuf[2*CACHELINESZ]; + uchar *p; + + p = (uchar*)ROUND((uintptr)cbuf, CACHELINESZ); + memset(p, 0, 4); + sdiorwext(fn, 0, p, 4, off|Sb32bit, 1); + if(SDIODEBUG) print("cfgreadl %lux: %2.2x %2.2x %2.2x %2.2x\n", off, p[0], p[1], p[2], p[3]); + return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24; +} + +static void +cfgwritel(int fn, ulong off, u32int data) +{ + uchar cbuf[2*CACHELINESZ]; + uchar *p; + int retry; + + p = (uchar*)ROUND((uintptr)cbuf, CACHELINESZ); + put4(p, data); + if(SDIODEBUG) print("cfgwritel %lux: %2.2x %2.2x %2.2x %2.2x\n", off, p[0], p[1], p[2], p[3]); + retry = 0; + while(waserror()){ + print("ether4330: cfgwritel retry %lux %ux\n", off, data); + sdioabort(fn); + if(++retry == 3) + nexterror(); + } + sdiorwext(fn, 1, p, 4, off|Sb32bit, 1); + poperror(); +} + +static void +sbwindow(ulong addr) +{ + addr &= ~(Sbwsize-1); + cfgw(Sbaddr, addr>>8); + cfgw(Sbaddr+1, addr>>16); + cfgw(Sbaddr+2, addr>>24); +} + +static void +sbrw(int fn, int write, uchar *buf, int len, ulong off) +{ + int n; + USED(fn); + + if(waserror()){ + print("ether4330: sbrw err off %lux len %ud\n", off, len); + nexterror(); + } + if(write){ + if(len >= 4){ + n = len; + n &= ~3; + sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1); + off += n; + buf += n; + len -= n; + } + while(len > 0){ + sdiowr(Fn1, off|Sb32bit, *buf); + off++; + buf++; + len--; + } + }else{ + if(len >= 4){ + n = len; + n &= ~3; + sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1); + off += n; + buf += n; + len -= n; + } + while(len > 0){ + *buf = sdiord(Fn1, off|Sb32bit); + off++; + buf++; + len--; + } + } + poperror(); +} + +static void +sbmem(int write, uchar *buf, int len, ulong off) +{ + ulong n; + + n = ROUNDUP(off, Sbwsize) - off; + if(n == 0) + n = Sbwsize; + while(len > 0){ + if(n > len) + n = len; + sbwindow(off); + sbrw(Fn1, write, buf, n, off & (Sbwsize-1)); + off += n; + buf += n; + len -= n; + n = Sbwsize; + } +} + +static void +packetrw(int write, uchar *buf, int len) +{ + int n; + int retry; + + n = 2048; + while(len > 0){ + if(n > len) + n = ROUND(len, 4); + retry = 0; + while(waserror()){ + sdioabort(Fn2); + if(++retry == 3) + nexterror(); + } + sdiorwext(Fn2, write, buf, n, Enumbase, 0); + poperror(); + buf += n; + len -= n; + } +} + +/* + * Configuration and control of chip cores via Silicon Backplane + */ + +static void +sbdisable(ulong regs, int pre, int ioctl) +{ + sbwindow(regs); + if((cfgreadl(Fn1, regs + Resetctrl) & 1) != 0){ + cfgwritel(Fn1, regs + Ioctrl, 3|ioctl); + cfgreadl(Fn1, regs + Ioctrl); + return; + } + cfgwritel(Fn1, regs + Ioctrl, 3|pre); + cfgreadl(Fn1, regs + Ioctrl); + cfgwritel(Fn1, regs + Resetctrl, 1); + microdelay(10); + while((cfgreadl(Fn1, regs + Resetctrl) & 1) == 0) + ; + cfgwritel(Fn1, regs + Ioctrl, 3|ioctl); + cfgreadl(Fn1, regs + Ioctrl); +} + +static void +sbreset(ulong regs, int pre, int ioctl) +{ + sbdisable(regs, pre, ioctl); + sbwindow(regs); + if(SBDEBUG) print("sbreset %#p %#lux %#lux ->", regs, + cfgreadl(Fn1, regs+Ioctrl), cfgreadl(Fn1, regs+Resetctrl)); + while((cfgreadl(Fn1, regs + Resetctrl) & 1) != 0){ + cfgwritel(Fn1, regs + Resetctrl, 0); + microdelay(40); + } + cfgwritel(Fn1, regs + Ioctrl, 1|ioctl); + cfgreadl(Fn1, regs + Ioctrl); + if(SBDEBUG) print("%#lux %#lux\n", + cfgreadl(Fn1, regs+Ioctrl), cfgreadl(Fn1, regs+Resetctrl)); +} + +static void +corescan(Ctlr *ctl, ulong r) +{ + uchar *buf; + int i, coreid, corerev; + ulong addr; + + buf = sdmalloc(Corescansz); + if(buf == nil) + error(Enomem); + sbmem(0, buf, Corescansz, r); + coreid = 0; + corerev = 0; + for(i = 0; i < Corescansz; i += 4){ + switch(buf[i]&0xF){ + case 0xF: /* end */ + sdfree(buf); + return; + case 0x1: /* core info */ + if((buf[i+4]&0xF) != 0x1) + break; + coreid = (buf[i+1] | buf[i+2]<<8) & 0xFFF; + i += 4; + corerev = buf[i+3]; + break; + case 0x05: /* address */ + addr = buf[i+1]<<8 | buf[i+2]<<16 | buf[i+3]<<24; + addr &= ~0xFFF; + if(SBDEBUG) print("core %x %s %#p\n", coreid, buf[i]&0xC0? "ctl" : "mem", addr); + switch(coreid){ + case 0x800: + if((buf[i] & 0xC0) == 0) + ctl->chipcommon = addr; + break; + case ARMcm3: + case ARM7tdmi: + case ARMcr4: + ctl->armcore = coreid; + if(buf[i] & 0xC0){ + if(ctl->armctl == 0) + ctl->armctl = addr; + }else{ + if(ctl->armregs == 0) + ctl->armregs = addr; + } + break; + case 0x80E: + if(buf[i] & 0xC0) + ctl->socramctl = addr; + else if(ctl->socramregs == 0) + ctl->socramregs = addr; + ctl->socramrev = corerev; + break; + case 0x829: + if((buf[i] & 0xC0) == 0) + ctl->sdregs = addr; + ctl->sdiorev = corerev; + break; + case 0x812: + if(buf[i] & 0xC0) + ctl->d11ctl = addr; + break; + } + } + } + sdfree(buf); +} + +static void +ramscan(Ctlr *ctl) +{ + ulong r, n, size; + int banks, i; + + if(ctl->armcore == ARMcr4){ + r = ctl->armregs; + sbwindow(r); + n = cfgreadl(Fn1, r + Cr4Cap); + if(SBDEBUG) print("cr4 banks %lux\n", n); + banks = ((n>>4) & 0xF) + (n & 0xF); + size = 0; + for(i = 0; i < banks; i++){ + cfgwritel(Fn1, r + Cr4Bankidx, i); + n = cfgreadl(Fn1, r + Cr4Bankinfo); + if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0x3F) + 1)); + size += 8192 * ((n & 0x3F) + 1); + } + ctl->socramsize = size; + ctl->rambase = 0x198000; + return; + } + if(ctl->socramrev <= 7 || ctl->socramrev == 12){ + print("ether4330: SOCRAM rev %d not supported\n", ctl->socramrev); + error(Eio); + } + sbreset(ctl->socramctl, 0, 0); + r = ctl->socramregs; + sbwindow(r); + n = cfgreadl(Fn1, r + Coreinfo); + if(SBDEBUG) print("socramrev %d coreinfo %lux\n", ctl->socramrev, n); + banks = (n>>4) & 0xF; + size = 0; + for(i = 0; i < banks; i++){ + cfgwritel(Fn1, r + Bankidx, i); + n = cfgreadl(Fn1, r + Bankinfo); + if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0x3F) + 1)); + size += 8192 * ((n & 0x3F) + 1); + } + ctl->socramsize = size; + ctl->rambase = 0; + if(ctl->chipid == 43430){ + cfgwritel(Fn1, r + Bankidx, 3); + cfgwritel(Fn1, r + Bankpda, 0); + } +} + +static void +sbinit(Ctlr *ctl) +{ + ulong r; + int chipid; + char buf[16]; + + sbwindow(Enumbase); + r = cfgreadl(Fn1, Enumbase); + chipid = r & 0xFFFF; + sprint(buf, chipid > 43000 ? "%d" : "%#x", chipid); + print("ether4330: chip %s rev %ld type %ld\n", buf, (r>>16)&0xF, (r>>28)&0xF); + switch(chipid){ + case 0x4330: + case 43362: + case 43430: + case 0x4345: + ctl->chipid = chipid; + ctl->chiprev = (r>>16)&0xF; + break; + default: + print("ether4330: chipid %#x (%d) not supported\n", chipid, chipid); + error(Eio); + } + r = cfgreadl(Fn1, Enumbase + 63*4); + corescan(ctl, r); + if(ctl->armctl == 0 || ctl->d11ctl == 0 || + (ctl->armcore == ARMcm3 && (ctl->socramctl == 0 || ctl->socramregs == 0))) + error("corescan didn't find essential cores\n"); + if(ctl->armcore == ARMcr4) + sbreset(ctl->armctl, Cr4Cpuhalt, Cr4Cpuhalt); + else + sbdisable(ctl->armctl, 0, 0); + sbreset(ctl->d11ctl, 8|4, 4); + ramscan(ctl); + if(SBDEBUG) print("ARM %#p D11 %#p SOCRAM %#p,%#p %lud bytes @ %#p\n", + ctl->armctl, ctl->d11ctl, ctl->socramctl, ctl->socramregs, ctl->socramsize, ctl->rambase); + cfgw(Clkcsr, 0); + microdelay(10); + if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr)); + cfgw(Clkcsr, Nohwreq | ReqALP); + while((cfgr(Clkcsr) & (HTavail|ALPavail)) == 0) + microdelay(10); + cfgw(Clkcsr, Nohwreq | ForceALP); + microdelay(65); + if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr)); + cfgw(Pullups, 0); + sbwindow(ctl->chipcommon); + cfgwritel(Fn1, ctl->chipcommon + Gpiopullup, 0); + cfgwritel(Fn1, ctl->chipcommon + Gpiopulldown, 0); + if(ctl->chipid != 0x4330 && ctl->chipid != 43362) + return; + cfgwritel(Fn1, ctl->chipcommon + Chipctladdr, 1); + if(cfgreadl(Fn1, ctl->chipcommon + Chipctladdr) != 1) + print("ether4330: can't set Chipctladdr\n"); + else{ + r = cfgreadl(Fn1, ctl->chipcommon + Chipctldata); + if(SBDEBUG) print("chipcommon PMU (%lux) %lux", cfgreadl(Fn1, ctl->chipcommon + Chipctladdr), r); + /* set SDIO drive strength >= 6mA */ + r &= ~0x3800; + if(ctl->chipid == 0x4330) + r |= 3<<11; + else + r |= 7<<11; + cfgwritel(Fn1, ctl->chipcommon + Chipctldata, r); + if(SBDEBUG) print("-> %lux (= %lux)\n", r, cfgreadl(Fn1, ctl->chipcommon + Chipctldata)); + } +} + +static void +sbenable(Ctlr *ctl) +{ + int i; + + if(SBDEBUG) print("enabling HT clock..."); + cfgw(Clkcsr, 0); + delay(1); + cfgw(Clkcsr, ReqHT); + for(i = 0; (cfgr(Clkcsr) & HTavail) == 0; i++){ + if(i == 50){ + print("ether4330: can't enable HT clock: csr %x\n", cfgr(Clkcsr)); + error(Eio); + } + tsleep(&up->sleep, return0, nil, 100); + } + cfgw(Clkcsr, cfgr(Clkcsr) | ForceHT); + delay(10); + if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr)); + sbwindow(ctl->sdregs); + cfgwritel(Fn1, ctl->sdregs + Sbmboxdata, 4 << 16); /* protocol version */ + cfgwritel(Fn1, ctl->sdregs + Intmask, FrameInt | MailboxInt | Fcchange); + sdioset(Fn0, Ioenable, 1<sleep, return0, nil, 100); + } + sdiowr(Fn0, Intenable, (1<genbuf, sizeof up->genbuf, "can't find %s in /boot or /sys/lib/firmware", file); + error(up->genbuf); + } + return c; +} + +static int +upload(Ctlr *ctl, char *file, int isconfig) +{ + Chan *c; + uchar *buf; + uchar *cbuf; + int off, n; + + buf = cbuf = nil; + c = findfirmware(file); + if(waserror()){ + cclose(c); + sdfree(buf); + sdfree(cbuf); + nexterror(); + } + buf = sdmalloc(Uploadsz); + if(buf == nil) + error(Enomem); + if(Firmwarecmp){ + cbuf = sdmalloc(Uploadsz); + if(cbuf == nil) + error(Enomem); + } + off = 0; + for(;;){ + n = devtab[c->type]->read(c, buf, Uploadsz, off); + if(n <= 0) + break; + if(isconfig){ + n = condense(buf, n); + off = ctl->socramsize - n - 4; + }else if(off == 0) + memmove(ctl->resetvec.c, buf, sizeof(ctl->resetvec.c)); + while(n&3) + buf[n++] = 0; + sbmem(1, buf, n, ctl->rambase + off); + if(isconfig) + break; + off += n; + } + if(Firmwarecmp){ + if(FWDEBUG) print("compare..."); + if(!isconfig) + off = 0; + for(;;){ + if(!isconfig){ + n = devtab[c->type]->read(c, buf, Uploadsz, off); + if(n <= 0) + break; + while(n&3) + buf[n++] = 0; + } + sbmem(0, cbuf, n, ctl->rambase + off); + if(memcmp(buf, cbuf, n) != 0){ + print("ether4330: firmware load failed offset %d\n", off); + error(Eio); + } + if(isconfig) + break; + off += n; + } + } + if(FWDEBUG) print("\n"); + poperror(); + cclose(c); + sdfree(buf); + sdfree(cbuf); + return n; +} + +/* + * Upload regulatory file (.clm) to firmware. + * Packet format is + * [2]flag [2]type [4]len [4]crc [len]data + */ +static void +reguload(Ctlr *ctl, char *file) +{ + Chan *c; + uchar *buf; + int off, n, flag; + enum { + Reguhdr = 2+2+4+4, + Regusz = 1400, + Regutyp = 2, + Flagclm = 1<<12, + Firstpkt= 1<<1, + Lastpkt = 1<<2, + }; + + buf = nil; + c = findfirmware(file); + if(waserror()){ + cclose(c); + free(buf); + nexterror(); + } + buf = malloc(Reguhdr+Regusz+1); + if(buf == nil) + error(Enomem); + put2(buf+2, Regutyp); + put2(buf+8, 0); + off = 0; + flag = Flagclm | Firstpkt; + while((flag&Lastpkt) == 0){ + n = devtab[c->type]->read(c, buf+Reguhdr, Regusz+1, off); + if(n <= 0) + break; + if(n == Regusz+1) + --n; + else{ + while(n&7) + buf[Reguhdr+n++] = 0; + flag |= Lastpkt; + } + put2(buf+0, flag); + put4(buf+4, n); + wlsetvar(ctl, "clmload", buf, Reguhdr + n); + off += n; + flag &= ~Firstpkt; + } + poperror(); + cclose(c); + free(buf); +} + +static void +fwload(Ctlr *ctl) +{ + uchar buf[4]; + uint i, n; + + i = 0; + while(firmware[i].chipid != ctl->chipid || + firmware[i].chiprev != ctl->chiprev){ + if(++i == nelem(firmware)){ + print("ether4330: no firmware for chipid %x (%d) chiprev %d\n", + ctl->chipid, ctl->chipid, ctl->chiprev); + error("no firmware"); + } + } + ctl->regufile = firmware[i].regufile; + cfgw(Clkcsr, ReqALP); + while((cfgr(Clkcsr) & ALPavail) == 0) + microdelay(10); + memset(buf, 0, 4); + sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4); + if(FWDEBUG) print("firmware load..."); + upload(ctl, firmware[i].fwfile, 0); + if(FWDEBUG) print("config load..."); + n = upload(ctl, firmware[i].cfgfile, 1); + n /= 4; + n = (n & 0xFFFF) | (~n << 16); + put4(buf, n); + sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4); + if(ctl->armcore == ARMcr4){ + sbwindow(ctl->sdregs); + cfgwritel(Fn1, ctl->sdregs + Intstatus, ~0); + if(ctl->resetvec.i != 0){ + if(SBDEBUG) print("%ux\n", ctl->resetvec.i); + sbmem(1, ctl->resetvec.c, sizeof(ctl->resetvec.c), 0); + } + sbreset(ctl->armctl, Cr4Cpuhalt, 0); + }else + sbreset(ctl->armctl, 0, 0); +} + +/* + * Communication of data and control packets + */ + +void +intwait(Ctlr *ctlr, int wait) +{ + ulong ints, mbox; + int i; + + if(waserror()) + return; + for(;;){ + sdiocardintr(wait); + sbwindow(ctlr->sdregs); + i = sdiord(Fn0, Intpend); + if(i == 0){ + tsleep(&up->sleep, return0, 0, 10); + continue; + } + ints = cfgreadl(Fn1, ctlr->sdregs + Intstatus); + cfgwritel(Fn1, ctlr->sdregs + Intstatus, ints); + if(0) print("INTS: (%x) %lux -> %lux\n", i, ints, cfgreadl(Fn1, ctlr->sdregs + Intstatus)); + if(ints & MailboxInt){ + mbox = cfgreadl(Fn1, ctlr->sdregs + Hostmboxdata); + cfgwritel(Fn1, ctlr->sdregs + Sbmbox, 2); /* ack */ + if(mbox & 0x8) + print("ether4330: firmware ready\n"); + } + if(ints & FrameInt) + break; + } + poperror(); +} + +static Block* +wlreadpkt(Ctlr *ctl) +{ + Block *b; + Sdpcm *p; + int len, lenck; + + b = allocb(2048); + p = (Sdpcm*)b->wp; + qlock(&ctl->pktlock); + for(;;){ + packetrw(0, b->wp, sizeof(*p)); + len = p->len[0] | p->len[1]<<8; + if(len == 0){ + freeb(b); + b = nil; + break; + } + lenck = p->lenck[0] | p->lenck[1]<<8; + if(lenck != (len ^ 0xFFFF) || + len < sizeof(*p) || len > 2048){ + print("ether4330: wlreadpkt error len %.4x lenck %.4x\n", len, lenck); + cfgw(Framectl, Rfhalt); + while(cfgr(Rfrmcnt+1)) + ; + while(cfgr(Rfrmcnt)) + ; + continue; + } + if(len > sizeof(*p)) + packetrw(0, b->wp + sizeof(*p), len - sizeof(*p)); + b->wp += len; + break; + } + qunlock(&ctl->pktlock); + return b; +} + +static void +txstart(Ether *edev) +{ + Ctlr *ctl; + Sdpcm *p; + Block *b; + int len, off; + + ctl = edev->ctlr; + if(!canqlock(&ctl->tlock)) + return; + if(waserror()){ + qunlock(&ctl->tlock); + return; + } + for(;;){ + lock(&ctl->txwinlock); + if(ctl->txseq == ctl->txwindow){ + //print("f"); + unlock(&ctl->txwinlock); + break; + } + if(ctl->fcmask & 1<<2){ + //print("x"); + unlock(&ctl->txwinlock); + break; + } + unlock(&ctl->txwinlock); + b = qget(edev->oq); + if(b == nil) + break; + off = ((uintptr)b->rp & 3) + sizeof(Sdpcm); + b = padblock(b, off + 4); + len = BLEN(b); + p = (Sdpcm*)b->rp; + memset(p, 0, off); /* TODO: refactor dup code */ + put2(p->len, len); + put2(p->lenck, ~len); + p->chanflg = 2; + p->seq = ctl->txseq; + p->doffset = off; + put4(b->rp + off, 0x20); /* BDC header */ + if(iodebug) dump("send", b->rp, len); + qlock(&ctl->pktlock); + if(waserror()){ + if(iodebug) print("halt frame %x %x\n", cfgr(Wfrmcnt+1), cfgr(Wfrmcnt+1)); + cfgw(Framectl, Wfhalt); + while(cfgr(Wfrmcnt+1)) + ; + while(cfgr(Wfrmcnt)) + ; + qunlock(&ctl->pktlock); + nexterror(); + } + packetrw(1, b->rp, len); + ctl->txseq++; + poperror(); + qunlock(&ctl->pktlock); + freeb(b); + } + poperror(); + qunlock(&ctl->tlock); +} + +static void +rproc(void *a) +{ + Ether *edev; + Ctlr *ctl; + Block *b; + Sdpcm *p; + Cmd *q; + int flowstart; + int bdc; + + edev = a; + ctl = edev->ctlr; + flowstart = 0; + for(;;){ + if(flowstart){ + //print("F"); + flowstart = 0; + txstart(edev); + } + b = wlreadpkt(ctl); + if(b == nil){ + intwait(ctl, 1); + continue; + } + p = (Sdpcm*)b->rp; + if(p->window != ctl->txwindow || p->fcmask != ctl->fcmask){ + lock(&ctl->txwinlock); + if(p->window != ctl->txwindow){ + if(ctl->txseq == ctl->txwindow) + flowstart = 1; + ctl->txwindow = p->window; + } + if(p->fcmask != ctl->fcmask){ + if((p->fcmask & 1<<2) == 0) + flowstart = 1; + ctl->fcmask = p->fcmask; + } + unlock(&ctl->txwinlock); + } + switch(p->chanflg & 0xF){ + case 0: + if(iodebug) dump("rsp", b->rp, BLEN(b)); + if(BLEN(b) < sizeof(Sdpcm) + sizeof(Cmd)) + break; + q = (Cmd*)(b->rp + sizeof(*p)); + if((q->id[0] | q->id[1]<<8) != ctl->reqid) + break; + ctl->rsp = b; + wakeup(&ctl->cmdr); + continue; + case 1: + if(iodebug) dump("event", b->rp, BLEN(b)); + if(BLEN(b) > p->doffset + 4){ + bdc = 4 + (b->rp[p->doffset + 3] << 2); + if(BLEN(b) > p->doffset + bdc){ + b->rp += p->doffset + bdc; /* skip BDC header */ + bcmevent(ctl, b->rp, BLEN(b)); + break; + } + } + if(iodebug && BLEN(b) != p->doffset) + print("short event %ld %d\n", BLEN(b), p->doffset); + break; + case 2: + if(iodebug) dump("packet", b->rp, BLEN(b)); + if(BLEN(b) > p->doffset + 4){ + bdc = 4 + (b->rp[p->doffset + 3] << 2); + if(BLEN(b) >= p->doffset + bdc + ETHERHDRSIZE){ + b->rp += p->doffset + bdc; /* skip BDC header */ + etheriq(edev, b, 1); + continue; + } + } + break; + default: + dump("ether4330: bad packet", b->rp, BLEN(b)); + break; + } + freeb(b); + } +} + +static void +linkdown(Ctlr *ctl) +{ + Ether *edev; + Netfile *f; + int i; + + edev = ctl->edev; + if(edev == nil || ctl->status != Connected) + return; + ctl->status = Disconnected; + /* send eof to aux/wpa */ + for(i = 0; i < edev->nfile; i++){ + f = edev->f[i]; + if(f == nil || f->in == nil || f->inuse == 0 || f->type != 0x888e) + continue; + qwrite(f->in, 0, 0); + } +} + +/* + * Command interface between host and firmware + */ + +static char *eventnames[] = { + [0] = "set ssid", + [1] = "join", + [2] = "start", + [3] = "auth", + [4] = "auth ind", + [5] = "deauth", + [6] = "deauth ind", + [7] = "assoc", + [8] = "assoc ind", + [9] = "reassoc", + [10] = "reassoc ind", + [11] = "disassoc", + [12] = "disassoc ind", + [13] = "quiet start", + [14] = "quiet end", + [15] = "beacon rx", + [16] = "link", + [17] = "mic error", + [18] = "ndis link", + [19] = "roam", + [20] = "txfail", + [21] = "pmkid cache", + [22] = "retrograde tsf", + [23] = "prune", + [24] = "autoauth", + [25] = "eapol msg", + [26] = "scan complete", + [27] = "addts ind", + [28] = "delts ind", + [29] = "bcnsent ind", + [30] = "bcnrx msg", + [31] = "bcnlost msg", + [32] = "roam prep", + [33] = "pfn net found", + [34] = "pfn net lost", + [35] = "reset complete", + [36] = "join start", + [37] = "roam start", + [38] = "assoc start", + [39] = "ibss assoc", + [40] = "radio", + [41] = "psm watchdog", + [44] = "probreq msg", + [45] = "scan confirm ind", + [46] = "psk sup", + [47] = "country code changed", + [48] = "exceeded medium time", + [49] = "icv error", + [50] = "unicast decode error", + [51] = "multicast decode error", + [52] = "trace", + [53] = "bta hci event", + [54] = "if", + [55] = "p2p disc listen complete", + [56] = "rssi", + [57] = "pfn scan complete", + [58] = "extlog msg", + [59] = "action frame", + [60] = "action frame complete", + [61] = "pre assoc ind", + [62] = "pre reassoc ind", + [63] = "channel adopted", + [64] = "ap started", + [65] = "dfs ap stop", + [66] = "dfs ap resume", + [67] = "wai sta event", + [68] = "wai msg", + [69] = "escan result", + [70] = "action frame off chan complete", + [71] = "probresp msg", + [72] = "p2p probreq msg", + [73] = "dcs request", + [74] = "fifo credit map", + [75] = "action frame rx", + [76] = "wake event", + [77] = "rm complete", + [78] = "htsfsync", + [79] = "overlay req", + [80] = "csa complete ind", + [81] = "excess pm wake event", + [82] = "pfn scan none", + [83] = "pfn scan allgone", + [84] = "gtk plumbed", + [85] = "assoc ind ndis", + [86] = "reassoc ind ndis", + [87] = "assoc req ie", + [88] = "assoc resp ie", + [89] = "assoc recreated", + [90] = "action frame rx ndis", + [91] = "auth req", + [92] = "tdls peer event", + [127] = "bcmc credit support" +}; + +static char* +evstring(uint event) +{ + static char buf[12]; + + if(event >= nelem(eventnames) || eventnames[event] == 0){ + /* not reentrant but only called from one kproc */ + snprint(buf, sizeof buf, "%d", event); + return buf; + } + return eventnames[event]; +} + +static void +bcmevent(Ctlr *ctl, uchar *p, int len) +{ + int flags; + long event, status, reason; + + if(len < ETHERHDRSIZE + 10 + 46) + return; + p += ETHERHDRSIZE + 10; /* skip bcm_ether header */ + len -= ETHERHDRSIZE + 10; + flags = nhgets(p + 2); + event = nhgets(p + 6); + status = nhgetl(p + 8); + reason = nhgetl(p + 12); + if(EVENTDEBUG) + print("ether4330: [%s] status %ld flags %#x reason %ld\n", + evstring(event), status, flags, reason); + switch(event){ + case 19: /* E_ROAM */ + if(status == 0) + break; + /* fall through */ + case 0: /* E_SET_SSID */ + ctl->joinstatus = 1 + status; + wakeup(&ctl->joinr); + break; + case 16: /* E_LINK */ + if(flags&1) /* link up */ + break; + /* fall through */ + case 5: /* E_DEAUTH */ + case 6: /* E_DEAUTH_IND */ + case 12: /* E_DISASSOC_IND */ + linkdown(ctl); + break; + case 26: /* E_SCAN_COMPLETE */ + break; + case 69: /* E_ESCAN_RESULT */ + wlscanresult(ctl->edev, p + 48, len - 48); + break; + default: + if(status){ + if(!EVENTDEBUG) + print("ether4330: [%s] error status %ld flags %#x reason %ld\n", + evstring(event), status, flags, reason); + dump("event", p, len); + } + } +} + +static int +joindone(void *a) +{ + return ((Ctlr*)a)->joinstatus; +} + +static int +waitjoin(Ctlr *ctl) +{ + int n; + + sleep(&ctl->joinr, joindone, ctl); + n = ctl->joinstatus; + ctl->joinstatus = 0; + return n - 1; +} + +static int +cmddone(void *a) +{ + return ((Ctlr*)a)->rsp != nil; +} + +static void +wlcmd(Ctlr *ctl, int write, int op, void *data, int dlen, void *res, int rlen) +{ + Block *b; + Sdpcm *p; + Cmd *q; + int len, tlen; + + if(write) + tlen = dlen + rlen; + else + tlen = MAX(dlen, rlen); + len = sizeof(Sdpcm) + sizeof(Cmd) + tlen; + b = allocb(len); + qlock(&ctl->cmdlock); + if(waserror()){ + freeb(b); + qunlock(&ctl->cmdlock); + nexterror(); + } + memset(b->wp, 0, len); + qlock(&ctl->pktlock); + p = (Sdpcm*)b->wp; + put2(p->len, len); + put2(p->lenck, ~len); + p->seq = ctl->txseq; + p->doffset = sizeof(Sdpcm); + b->wp += sizeof(*p); + + q = (Cmd*)b->wp; + put4(q->cmd, op); + put4(q->len, tlen); + put2(q->flags, write? 2 : 0); + put2(q->id, ++ctl->reqid); + put4(q->status, 0); + b->wp += sizeof(*q); + + if(dlen > 0) + memmove(b->wp, data, dlen); + if(write) + memmove(b->wp + dlen, res, rlen); + b->wp += tlen; + + if(iodebug) dump("cmd", b->rp, len); + packetrw(1, b->rp, len); + ctl->txseq++; + qunlock(&ctl->pktlock); + freeb(b); + b = nil; + USED(b); + sleep(&ctl->cmdr, cmddone, ctl); + b = ctl->rsp; + ctl->rsp = nil; + assert(b != nil); + p = (Sdpcm*)b->rp; + q = (Cmd*)(b->rp + p->doffset); + if(q->status[0] | q->status[1] | q->status[2] | q->status[3]){ + print("ether4330: cmd %d error status %ld\n", op, get4(q->status)); + dump("ether4330: cmd error", b->rp, BLEN(b)); + error("wlcmd error"); + } + if(!write) + memmove(res, q + 1, rlen); + freeb(b); + qunlock(&ctl->cmdlock); + poperror(); +} + +static void +wlcmdint(Ctlr *ctl, int op, int val) +{ + uchar buf[4]; + + put4(buf, val); + wlcmd(ctl, 1, op, buf, 4, nil, 0); +} + +static void +wlgetvar(Ctlr *ctl, char *name, void *val, int len) +{ + wlcmd(ctl, 0, GetVar, name, strlen(name) + 1, val, len); +} + +static void +wlsetvar(Ctlr *ctl, char *name, void *val, int len) +{ + if(VARDEBUG){ + char buf[32]; + snprint(buf, sizeof buf, "wlsetvar %s:", name); + dump(buf, val, len); + } + wlcmd(ctl, 1, SetVar, name, strlen(name) + 1, val, len); +} + +static void +wlsetint(Ctlr *ctl, char *name, int val) +{ + uchar buf[4]; + + put4(buf, val); + wlsetvar(ctl, name, buf, 4); +} + +static void +wlwepkey(Ctlr *ctl, int i) +{ + uchar params[164]; + uchar *p; + + memset(params, 0, sizeof params); + p = params; + p = put4(p, i); /* index */ + p = put4(p, ctl->keys[i].len); + memmove(p, ctl->keys[i].dat, ctl->keys[i].len); + p += 32 + 18*4; /* keydata, pad */ + if(ctl->keys[i].len == WMinKeyLen) + p = put4(p, 1); /* algo = WEP1 */ + else + p = put4(p, 3); /* algo = WEP128 */ + put4(p, 2); /* flags = Primarykey */ + + wlsetvar(ctl, "wsec_key", params, sizeof params); +} + +static void +memreverse(char *dst, char *src, int len) +{ + src += len; + while(len-- > 0) + *dst++ = *--src; +} + +static void +wlwpakey(Ctlr *ctl, int id, uvlong iv, uchar *ea) +{ + uchar params[164]; + uchar *p; + int pairwise; + + if(id == CMrxkey) + return; + pairwise = (id == CMrxkey || id == CMtxkey); + memset(params, 0, sizeof params); + p = params; + if(pairwise) + p = put4(p, 0); + else + p = put4(p, id - CMrxkey0); /* group key id */ + p = put4(p, ctl->keys[0].len); + memmove((char*)p, ctl->keys[0].dat, ctl->keys[0].len); + p += 32 + 18*4; /* keydata, pad */ + if(ctl->cryptotype == Wpa) + p = put4(p, 2); /* algo = TKIP */ + else + p = put4(p, 4); /* algo = AES_CCM */ + if(pairwise) + p = put4(p, 0); + else + p = put4(p, 2); /* flags = Primarykey */ + p += 3*4; + p = put4(p, 0); //pairwise); /* iv initialised */ + p += 4; + p = put4(p, iv>>16); /* iv high */ + p = put2(p, iv&0xFFFF); /* iv low */ + p += 2 + 2*4; /* align, pad */ + if(pairwise) + memmove(p, ea, Eaddrlen); + + wlsetvar(ctl, "wsec_key", params, sizeof params); +} + +static void +wljoin(Ctlr *ctl, char *ssid, int chan) +{ + uchar params[72]; + uchar *p; + int n; + + if(chan != 0) + chan |= 0x2b00; /* 20Mhz channel width */ + p = params; + n = strlen(ssid); + n = MIN(n, 32); + p = put4(p, n); + memmove(p, ssid, n); + memset(p + n, 0, 32 - n); + p += 32; + p = put4(p, 0xff); /* scan type */ + if(chan != 0){ + p = put4(p, 2); /* num probes */ + p = put4(p, 120); /* active time */ + p = put4(p, 390); /* passive time */ + }else{ + p = put4(p, -1); /* num probes */ + p = put4(p, -1); /* active time */ + p = put4(p, -1); /* passive time */ + } + p = put4(p, -1); /* home time */ + memset(p, 0xFF, Eaddrlen); /* bssid */ + p += Eaddrlen; + p = put2(p, 0); /* pad */ + if(chan != 0){ + p = put4(p, 1); /* num chans */ + p = put2(p, chan); /* chan spec */ + p = put2(p, 0); /* pad */ + assert(p == params + sizeof(params)); + }else{ + p = put4(p, 0); /* num chans */ + assert(p == params + sizeof(params) - 4); + } + + wlsetvar(ctl, "join", params, chan? sizeof params : sizeof params - 4); + ctl->status = Connecting; + switch(waitjoin(ctl)){ + case 0: + ctl->status = Connected; + break; + case 3: + ctl->status = Disconnected; + error("wifi join: network not found"); + case 1: + ctl->status = Disconnected; + error("wifi join: failed"); + default: + ctl->status = Disconnected; + error("wifi join: error"); + } +} + +static void +wlscanstart(Ctlr *ctl) +{ + /* version[4] action[2] sync_id[2] ssidlen[4] ssid[32] bssid[6] bss_type[1] + scan_type[1] nprobes[4] active_time[4] passive_time[4] home_time[4] + nchans[2] nssids[2] chans[nchans][2] ssids[nssids][32] */ + /* hack - this is only correct on a little-endian cpu */ + static uchar params[4+2+2+4+32+6+1+1+4*4+2+2+14*2+32+4] = { + 1,0,0,0, + 1,0, + 0x34,0x12, + 0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0xff,0xff,0xff,0xff,0xff,0xff, + 2, + 0, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, + 14,0, + 1,0, + 0x01,0x2b,0x02,0x2b,0x03,0x2b,0x04,0x2b,0x05,0x2e,0x06,0x2e,0x07,0x2e, + 0x08,0x2b,0x09,0x2b,0x0a,0x2b,0x0b,0x2b,0x0c,0x2b,0x0d,0x2b,0x0e,0x2b, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + }; + + wlcmdint(ctl, 49, 0); /* PASSIVE_SCAN */ + wlsetvar(ctl, "escan", params, sizeof params); +} + +static uchar* +gettlv(uchar *p, uchar *ep, int tag) +{ + int len; + + while(p + 1 < ep){ + len = p[1]; + if(p + 2 + len > ep) + return nil; + if(p[0] == tag) + return p; + p += 2 + len; + } + return nil; +} + +static void +addscan(Block *bp, uchar *p, int len) +{ + char bssid[24]; + char *auth, *auth2; + uchar *t, *et; + int ielen; + static uchar wpaie1[4] = { 0x00, 0x50, 0xf2, 0x01 }; + + snprint(bssid, sizeof bssid, ";bssid=%E", p + 8); + if(strstr((char*)bp->rp, bssid) != nil) + return; + bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim, + "ssid=%.*s%s;signal=%d;noise=%d;chan=%d", + p[18], (char*)p+19, bssid, + (short)get2(p+78), (signed char)p[80], + get2(p+72) & 0xF); + auth = auth2 = ""; + if(get2(p + 16) & 0x10) + auth = ";wep"; + ielen = get4(p + 0x78); + if(ielen > 0){ + t = p + get4(p + 0x74); + et = t + ielen; + if(et > p + len) + return; + if(gettlv(t, et, 0x30) != nil){ + auth = ""; + auth2 = ";wpa2"; + } + while((t = gettlv(t, et, 0xdd)) != nil){ + if(t[1] > 4 && memcmp(t+2, wpaie1, 4) == 0){ + auth = ";wpa"; + break; + } + t += 2 + t[1]; + } + } + bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim, + "%s%s\n", auth, auth2); +} + + +static void +wlscanresult(Ether *edev, uchar *p, int len) +{ + Ctlr *ctlr; + Netfile **ep, *f, **fp; + Block *bp; + int nbss, i; + + ctlr = edev->ctlr; + if(get4(p) > len) + return; + /* TODO: more syntax checking */ + bp = ctlr->scanb; + if(bp == nil) + ctlr->scanb = bp = allocb(8192); + nbss = get2(p+10); + p += 12; + len -= 12; + if(0) dump("SCAN", p, len); + if(nbss){ + addscan(bp, p, len); + return; + } + i = edev->scan; + ep = &edev->f[Ntypes]; + for(fp = edev->f; fp < ep && i > 0; fp++){ + f = *fp; + if(f == nil || f->scan == 0) + continue; + if(i == 1) + qpass(f->in, bp); + else + qpass(f->in, copyblock(bp, BLEN(bp))); + i--; + } + if(i) + freeb(bp); + ctlr->scanb = nil; +} + +static void +lproc(void *a) +{ + Ether *edev; + Ctlr *ctlr; + int secs; + + edev = a; + ctlr = edev->ctlr; + secs = 0; + for(;;){ + tsleep(&up->sleep, return0, 0, 1000); + if(ctlr->scansecs){ + if(secs == 0){ + if(waserror()) + ctlr->scansecs = 0; + else{ + wlscanstart(ctlr); + poperror(); + } + secs = ctlr->scansecs; + } + --secs; + }else + secs = 0; + } +} + +static void +wlinit(Ether *edev, Ctlr *ctlr) +{ + uchar ea[Eaddrlen]; + uchar eventmask[16]; + char version[128]; + char *p; + static uchar keepalive[12] = {1, 0, 11, 0, 0xd8, 0xd6, 0, 0, 0, 0, 0, 0}; + + wlgetvar(ctlr, "cur_etheraddr", ea, Eaddrlen); + memmove(edev->ea, ea, Eaddrlen); + memmove(edev->addr, ea, Eaddrlen); + print("ether4330: addr %E\n", edev->ea); + wlsetint(ctlr, "assoc_listen", 10); + if(ctlr->chipid == 43430 || ctlr->chipid == 0x4345) + wlcmdint(ctlr, 0x56, 0); /* powersave off */ + else + wlcmdint(ctlr, 0x56, 2); /* powersave FAST */ + wlsetint(ctlr, "bus:txglom", 0); + wlsetint(ctlr, "bcn_timeout", 10); + wlsetint(ctlr, "assoc_retry_max", 3); + if(ctlr->chipid == 0x4330){ + wlsetint(ctlr, "btc_wire", 4); + wlsetint(ctlr, "btc_mode", 1); + wlsetvar(ctlr, "mkeep_alive", keepalive, 11); + } + memset(eventmask, 0xFF, sizeof eventmask); +#define ENABLE(n) eventmask[n/8] |= 1<<(n%8) +#define DISABLE(n) eventmask[n/8] &= ~(1<<(n%8)) + DISABLE(40); /* E_RADIO */ + DISABLE(44); /* E_PROBREQ_MSG */ + DISABLE(54); /* E_IF */ + DISABLE(71); /* E_PROBRESP_MSG */ + DISABLE(20); /* E_TXFAIL */ + DISABLE(124); /* ? */ + wlsetvar(ctlr, "event_msgs", eventmask, sizeof eventmask); + wlcmdint(ctlr, 0xb9, 0x28); /* SET_SCAN_CHANNEL_TIME */ + wlcmdint(ctlr, 0xbb, 0x28); /* SET_SCAN_UNASSOC_TIME */ + wlcmdint(ctlr, 0x102, 0x82); /* SET_SCAN_PASSIVE_TIME */ + wlcmdint(ctlr, 2, 0); /* UP */ + memset(version, 0, sizeof version); + wlgetvar(ctlr, "ver", version, sizeof version - 1); + if((p = strchr(version, '\n')) != nil) + *p = '\0'; + if(0) print("ether4330: %s\n", version); + wlsetint(ctlr, "roam_off", 1); + wlcmdint(ctlr, 0x14, 1); /* SET_INFRA 1 */ + wlcmdint(ctlr, 10, 0); /* SET_PROMISC */ + //wlcmdint(ctlr, 0x8e, 0); /* SET_BAND 0 */ + //wlsetint(ctlr, "wsec", 1); + wlcmdint(ctlr, 2, 1); /* UP */ + ctlr->keys[0].len = WMinKeyLen; + //wlwepkey(ctlr, 0); +} + +/* + * Plan 9 driver interface + */ + +static long +etherbcmifstat(Ether* edev, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *p; + int l; + static char *cryptoname[4] = { + [0] "off", + [Wep] "wep", + [Wpa] "wpa", + [Wpa2] "wpa2", + }; + /* these strings are known by aux/wpa */ + static char* connectstate[] = { + [Disconnected] = "unassociated", + [Connecting] = "connecting", + [Connected] = "associated", + }; + + ctlr = edev->ctlr; + if(ctlr == nil) + return 0; + p = malloc(READSTR); + l = 0; + + l += snprint(p+l, READSTR-l, "channel: %d\n", ctlr->chanid); + l += snprint(p+l, READSTR-l, "essid: %s\n", ctlr->essid); + l += snprint(p+l, READSTR-l, "crypt: %s\n", cryptoname[ctlr->cryptotype]); + l += snprint(p+l, READSTR-l, "oq: %d\n", qlen(edev->oq)); + l += snprint(p+l, READSTR-l, "txwin: %d\n", ctlr->txwindow); + l += snprint(p+l, READSTR-l, "txseq: %d\n", ctlr->txseq); + l += snprint(p+l, READSTR-l, "status: %s\n", connectstate[ctlr->status]); + USED(l); + n = readstr(offset, a, n, p); + free(p); + return n; +} + +static void +etherbcmtransmit(Ether *edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + if(ctlr == nil) + return; + txstart(edev); +} + +static int +parsehex(char *buf, int buflen, char *a) +{ + int i, k, n; + + k = 0; + for(i = 0;k < buflen && *a; i++){ + if(*a >= '0' && *a <= '9') + n = *a++ - '0'; + else if(*a >= 'a' && *a <= 'f') + n = *a++ - 'a' + 10; + else if(*a >= 'A' && *a <= 'F') + n = *a++ - 'A' + 10; + else + break; + + if(i & 1){ + buf[k] |= n; + k++; + } + else + buf[k] = n<<4; + } + if(i & 1) + return -1; + return k; +} + +static int +wepparsekey(WKey* key, char* a) +{ + int i, k, len, n; + char buf[WMaxKeyLen]; + + len = strlen(a); + if(len == WMinKeyLen || len == WMaxKeyLen){ + memset(key->dat, 0, sizeof(key->dat)); + memmove(key->dat, a, len); + key->len = len; + + return 0; + } + else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){ + k = 0; + for(i = 0; i < len; i++){ + if(*a >= '0' && *a <= '9') + n = *a++ - '0'; + else if(*a >= 'a' && *a <= 'f') + n = *a++ - 'a' + 10; + else if(*a >= 'A' && *a <= 'F') + n = *a++ - 'A' + 10; + else + return -1; + + if(i & 1){ + buf[k] |= n; + k++; + } + else + buf[k] = n<<4; + } + + memset(key->dat, 0, sizeof(key->dat)); + memmove(key->dat, buf, k); + key->len = k; + + return 0; + } + + return -1; +} + +static int +wpaparsekey(WKey *key, uvlong *ivp, char *a) +{ + int len; + char *e; + + if(cistrncmp(a, "tkip:", 5) == 0 || cistrncmp(a, "ccmp:", 5) == 0) + a += 5; + else + return 1; + len = parsehex(key->dat, sizeof(key->dat), a); + if(len <= 0) + return 1; + key->len = len; + a += 2*len; + if(*a++ != '@') + return 1; + *ivp = strtoull(a, &e, 16); + if(e == a) + return -1; + return 0; +} + +static void +setauth(Ctlr *ctlr, Cmdbuf *cb, char *a) +{ + uchar wpaie[32]; + int i; + + i = parsehex((char*)wpaie, sizeof wpaie, a); + if(i < 2 || i != wpaie[1] + 2) + cmderror(cb, "bad wpa ie syntax"); + if(wpaie[0] == 0xdd) + ctlr->cryptotype = Wpa; + else if(wpaie[0] == 0x30) + ctlr->cryptotype = Wpa2; + else + cmderror(cb, "bad wpa ie"); + wlsetvar(ctlr, "wpaie", wpaie, i); + if(ctlr->cryptotype == Wpa){ + wlsetint(ctlr, "wpa_auth", 4|2); /* auth_psk | auth_unspecified */ + wlsetint(ctlr, "auth", 0); + wlsetint(ctlr, "wsec", 2); /* tkip */ + wlsetint(ctlr, "wpa_auth", 4); /* auth_psk */ + }else{ + wlsetint(ctlr, "wpa_auth", 0x80|0x40); /* auth_psk | auth_unspecified */ + wlsetint(ctlr, "auth", 0); + wlsetint(ctlr, "wsec", 4); /* aes */ + wlsetint(ctlr, "wpa_auth", 0x80); /* auth_psk */ + } +} + +static int +setcrypt(Ctlr *ctlr, Cmdbuf*, char *a) +{ + if(cistrcmp(a, "wep") == 0 || cistrcmp(a, "on") == 0) + ctlr->cryptotype = Wep; + else if(cistrcmp(a, "off") == 0 || cistrcmp(a, "none") == 0) + ctlr->cryptotype = 0; + else + return 0; + wlsetint(ctlr, "auth", ctlr->cryptotype); + return 1; +} + +static long +etherbcmctl(Ether* edev, void* buf, long n) +{ + Ctlr *ctlr; + Cmdbuf *cb; + Cmdtab *ct; + uchar ea[Eaddrlen]; + uvlong iv; + int i; + + if((ctlr = edev->ctlr) == nil) + error(Enonexist); + USED(ctlr); + + cb = parsecmd(buf, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, cmds, nelem(cmds)); + switch(ct->index){ + case CMauth: + setauth(ctlr, cb, cb->f[1]); + if(ctlr->essid[0]) + wljoin(ctlr, ctlr->essid, ctlr->chanid); + break; + case CMchannel: + if((i = atoi(cb->f[1])) < 0 || i > 16) + cmderror(cb, "bad channel number"); + //wlcmdint(ctlr, 30, i); /* SET_CHANNEL */ + ctlr->chanid = i; + break; + case CMcrypt: + if(setcrypt(ctlr, cb, cb->f[1])){ + if(ctlr->essid[0]) + wljoin(ctlr, ctlr->essid, ctlr->chanid); + }else + cmderror(cb, "bad crypt type"); + break; + case CMessid: + if(cistrcmp(cb->f[1], "default") == 0) + memset(ctlr->essid, 0, sizeof(ctlr->essid)); + else{ + strncpy(ctlr->essid, cb->f[1], sizeof(ctlr->essid) - 1); + ctlr->essid[sizeof(ctlr->essid) - 1] = '\0'; + } + if(!waserror()){ + wljoin(ctlr, ctlr->essid, ctlr->chanid); + poperror(); + } + break; + case CMjoin: /* join essid channel wep|on|off|wpakey */ + if(strcmp(cb->f[1], "") != 0){ /* empty string for no change */ + if(cistrcmp(cb->f[1], "default") != 0){ + strncpy(ctlr->essid, cb->f[1], sizeof(ctlr->essid)-1); + ctlr->essid[sizeof(ctlr->essid)-1] = 0; + }else + memset(ctlr->essid, 0, sizeof(ctlr->essid)); + }else if(ctlr->essid[0] == 0) + cmderror(cb, "essid not set"); + if((i = atoi(cb->f[2])) >= 0 && i <= 16) + ctlr->chanid = i; + else + cmderror(cb, "bad channel number"); + if(!setcrypt(ctlr, cb, cb->f[3])) + setauth(ctlr, cb, cb->f[3]); + if(ctlr->essid[0]) + wljoin(ctlr, ctlr->essid, ctlr->chanid); + break; + case CMkey1: + case CMkey2: + case CMkey3: + case CMkey4: + i = ct->index - CMkey1; + if(wepparsekey(&ctlr->keys[i], cb->f[1])) + cmderror(cb, "bad WEP key syntax"); + wlsetint(ctlr, "wsec", 1); /* wep enabled */ + wlwepkey(ctlr, i); + break; + case CMrxkey: + case CMrxkey0: + case CMrxkey1: + case CMrxkey2: + case CMrxkey3: + case CMtxkey: + if(parseether(ea, cb->f[1]) < 0) + cmderror(cb, "bad ether addr"); + if(wpaparsekey(&ctlr->keys[0], &iv, cb->f[2])) + cmderror(cb, "bad wpa key"); + wlwpakey(ctlr, ct->index, iv, ea); + break; + case CMdebug: + iodebug = atoi(cb->f[1]); + break; + } + poperror(); + free(cb); + return n; +} + +static void +etherbcmscan(void *a, uint secs) +{ + Ether* edev; + Ctlr* ctlr; + + edev = a; + ctlr = edev->ctlr; + ctlr->scansecs = secs; +} + +static void +etherbcmattach(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(waserror()){ + //print("ether4330: attach failed: %s\n", up->errstr); + qunlock(&ctlr->alock); + nexterror(); + } + if(ctlr->edev == nil){ + if(ctlr->chipid == 0){ + sdioinit(); + sbinit(ctlr); + } + fwload(ctlr); + sbenable(ctlr); + kproc("wifireader", rproc, edev); + kproc("wifitimer", lproc, edev); + if(ctlr->regufile) + reguload(ctlr, ctlr->regufile); + wlinit(edev, ctlr); + ctlr->edev = edev; + } + qunlock(&ctlr->alock); + poperror(); +} + +static void +etherbcmshutdown(Ether*) +{ + sdioreset(); +} + + +static int +etherbcmpnp(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = malloc(sizeof(Ctlr)); + ctlr->chanid = Wifichan; + edev->ctlr = ctlr; + edev->attach = etherbcmattach; + edev->transmit = etherbcmtransmit; + edev->ifstat = etherbcmifstat; + edev->ctl = etherbcmctl; + edev->scanbs = etherbcmscan; + edev->shutdown = etherbcmshutdown; + edev->arg = edev; + + return 0; +} + +void +ether4330link(void) +{ + addethercard("4330", etherbcmpnp); +} diff -Nru /n/sources/plan9/sys/src/9/bcm/ethergenet.c /sys/src/9/bcm/ethergenet.c --- /n/sources/plan9/sys/src/9/bcm/ethergenet.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/ethergenet.c Sun Apr 11 00:00:00 2021 @@ -0,0 +1,971 @@ +/* + * Broadcom 2711 native gigabit ethernet + * + * from 9front + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/netif.h" +#include "etherif.h" +#include "ethermii.h" + +#define MIIDBG if(0)print + +enum +{ + Rbsz = 10240, + Maxtu = 9014, + + DmaOWN = 0x8000, + DmaSOP = 0x2000, + DmaEOP = 0x4000, + DmaRxLg = 0x10, + DmaRxNo = 0x08, + DmaRxErr = 0x04, + DmaRxCrc = 0x02, + DmaRxOv = 0x01, + DmaRxErrors = DmaRxLg|DmaRxNo|DmaRxErr|DmaRxCrc|DmaRxOv, + + DmaTxQtag = 0x1F80, + DmaTxUnderrun = 0x0200, + DmaTxAppendCrc = 0x0040, + DmaTxOwCrc = 0x0020, + DmaTxDoCsum = 0x0010, + + /* Ctlr->regs */ + SysRevision = 0x00/4, + SysPortCtrl = 0x04/4, + PortModeIntEphy = 0, + PortModeIntGphy = 1, + PortModeExtEphy = 2, + PortModeExtGphy = 3, + PortModeExtRvmii50 = 4, + PortModeExtRvmii25 = 16 | 4, + LedActSourceMac = 1 << 9, + + SysRbufFlushCtrl = 0x08/4, + SysTbufFlushCtrl = 0x0C/4, + + ExtRgmiiOobCtrl = 0x8C/4, + RgmiiLink = 1 << 4, + OobDisable = 1 << 5, + RgmiiModeEn = 1 << 6, + IdModeDis = 1 << 16, + + Intrl0 = 0x200/4, + IrqScb = 1 << 0, + IrqEphy = 1 << 1, + IrqPhyDetR = 1 << 2, + IrqPhyDetF = 1 << 3, + IrqLinkUp = 1 << 4, + IrqLinkDown = 1 << 5, + IrqUmac = 1 << 6, + IrqUmacTsv = 1 << 7, + IrqTbufUnderrun = 1 << 8, + IrqRbufOverflow = 1 << 9, + IrqHfbSm = 1 << 10, + IrqHfbMm = 1 << 11, + IrqMpdR = 1 << 12, + IrqRxDmaDone = 1 << 13, + IrqRxDmaPDone = 1 << 14, + IrqRxDmaBDone = 1 << 15, + IrqTxDmaDone = 1 << 16, + IrqTxDmaPDone = 1 << 17, + IrqTxDmaBDone = 1 << 18, + IrqMdioDone = 1 << 23, + IrqMdioError = 1 << 24, + Intrl1 = 0x240/4, + /* Intrl0/1 + ... */ + IntrSts = 0x00/4, + IntrSet = 0x04/4, + IntrClr = 0x08/4, + IntrMaskSts = 0x0C/4, + IntrMaskSet = 0x10/4, + IntrMaskClr = 0x14/4, + + RbufCtrl = 0x300/4, + Rbuf64En = 1 << 0, + RbufAlign2B = 1 << 1, + RbufBadDis = 1 << 2, + + RbufChkCtrl = 0x314/4, + RbufChkRxChkEn = 1 << 0, + RbufChkSkipFcs = 1 << 4, + + RbufOvflCnt = 0x394/4, + RbufErrCnt = 0x398/4, + + RbufEnergyCtrl = 0x39c/4, + RbufEeeEn = 1 << 0, + RbufPmEn = 1 << 1, + + RbufTbufSizeCtrl= 0x3b4/4, + + TbufCtrl = 0x600/4, + TbufBpMc = 0x60C/4, + TbufEnergyCtrl = 0x614/4, + + UmacCmd = 0x808/4, + CmdTxEn = 1 << 0, + CmdRxEn = 1 << 1, + CmdSpeed10 = 0 << 2, + CmdSpeed100 = 1 << 2, + CmdSpeed1000 = 2 << 2, + CmdSpeedMask = 3 << 2, + CmdProm = 1 << 4, + CmdPadEn = 1 << 5, + CmdCrcFwd = 1 << 6, + CmdPauseFwd = 1 << 7, + CmdRxPauseIgn = 1 << 8, + CmdTxAddrIn = 1 << 9, + CmdHdEn = 1 << 10, + CmdSwReset = 1 << 13, + CmdLclLoopEn = 1 << 15, + CmdAutoConfig = 1 << 22, + CmdCntlFrmEn = 1 << 23, + CmdNoLenChk = 1 << 24, + CmdRmtLoopEn = 1 << 25, + CmdPrblEn = 1 << 27, + CmdTxPauseIgn = 1 << 28, + CmdTxRxEn = 1 << 29, + CmdRuntFilterDis= 1 << 30, + + UmacMac0 = 0x80C/4, + UmacMac1 = 0x810/4, + UmacMaxFrameLen = 0x814/4, + + UmacEeeCtrl = 0x864/4, + UmacEeeEn = 1<<3, + + UmacEeeLpiTimer = 0x868/4, + UmacEeeWakeTimer= 0x86C/4, + UmacEeeRefCount = 0x870/4, + EeeRefCountMask = 0xFFFF, + + UmacTxFlush = 0xb34/4, + + UmacMibCtrl = 0xd80/4, + MibResetRx = 1 << 0, + MibResetRunt = 1 << 1, + MibResetTx = 1 << 2, + + MdioCmd = 0xe14/4, + MdioStartBusy = 1 << 29, + MdioReadFail = 1 << 28, + MdioRead = 2 << 26, + MdioWrite = 1 << 26, + MdioPhyShift = 21, + MdioPhyMask = 0x1F, + MdioAddrShift = 16, + MdioAddrMask = 0x1F, + + UmacMpdCtrl = 0xe20/4, + MpdEn = 1 << 0, + MpdPwEn = 1 << 27, + + UmacMdfCtrl = 0xe50/4, + UmacMdfAddr0 = 0xe54/4, + + RdmaOffset = 0x2000/4, + TdmaOffset = 0x4000/4, + HfbOffset = 0x8000/4, + + HfbCtlr = 0xFC00/4, + HfbFltEnable = 0xFC04/4, + HfbFltLen = 0xFC1C/4, + + /* common Ring->regs */ + RdmaWP = 0x00/4, + TdmaRP = 0x00/4, + RxWP = 0x08/4, + TxRP = 0x08/4, + TxWP = 0x0C/4, + RxRP = 0x0C/4, + DmaRingBufSize = 0x10/4, + DmaStart = 0x14/4, + DmaEnd = 0x1C/4, + DmaDoneThresh = 0x24/4, + TdmaFlowPeriod = 0x28/4, + RdmaXonXoffThresh=0x28/4, + TdmaWP = 0x2C/4, + RdmaRP = 0x2C/4, + + /* + * reg offsets only for RING16 + * ctlr->rx->regs / ctlr->tx->regs + */ + RingCfg = 0x40/4, + RxRingCfgMask = 0x10000, + TxRingCfgMask = 0x1000F, + + DmaCtrl = 0x44/4, + DmaCtrlEn = 1 << 0, + DmaStatus = 0x48/4, + DmaStatusDis = 1 << 0, + DmaScbBurstSize = 0x4C/4, + + TdmaArbCtrl = 0x6C/4, + TdmaPriority0 = 0x70/4, + TdmaPriority1 = 0x74/4, + TdmaPriority2 = 0x78/4, + + RdmaTimeout0 = 0x6C/4, + RdmaIndex2Ring0 = 0xB0/4, +}; + +typedef struct Desc Desc; +typedef struct Ring Ring; +typedef struct Ctlr Ctlr; + +struct Desc +{ + u32int *d; /* hw descriptor */ + Block *b; +}; + +struct Ring +{ + Rendez; + u32int *regs; + u32int *intregs; + u32int intmask; + + Desc *d; + + u32int m; + u32int cp; + u32int rp; + u32int wp; + + int num; +}; + +struct Ctlr +{ + Lock; + u32int *regs; + + Desc rd[256]; + Desc td[256]; + + Ring rx[1+0]; + Ring tx[1+0]; + + Rendez avail[1]; + Rendez link[1]; + struct { + Mii; + Rendez; + } mii[1]; + + QLock; + char attached; +}; + +static Block *scratch; + +#define REG(x) (x) + +static void +interrupt0(Ureg*, void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + u32int sts; + + sts = REG(ctlr->regs[Intrl0 + IntrSts]) & ~REG(ctlr->regs[Intrl0 + IntrMaskSts]); + REG(ctlr->regs[Intrl0 + IntrClr]) = sts; + REG(ctlr->regs[Intrl0 + IntrMaskSet]) = sts; + + if(sts & ctlr->rx->intmask) + wakeup(ctlr->rx); + if(sts & ctlr->tx->intmask) + wakeup(ctlr->tx); + + if(sts & (IrqMdioDone|IrqMdioError)) + wakeup(ctlr->mii); + if(sts & (IrqLinkUp|IrqLinkDown)) + wakeup(ctlr->link); +} + +static void +interrupt1(Ureg*, void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + u32int sts; + int i; + + sts = REG(ctlr->regs[Intrl1 + IntrSts]) & ~REG(ctlr->regs[Intrl1 + IntrMaskSts]); + REG(ctlr->regs[Intrl1 + IntrClr]) = sts; + REG(ctlr->regs[Intrl1 + IntrMaskSet]) = sts; + + for(i = 1; i < nelem(ctlr->rx); i++) + if(sts & ctlr->rx[i].intmask) + wakeup(&ctlr->rx[i]); + + for(i = 1; i < nelem(ctlr->tx); i++) + if(sts & ctlr->tx[i].intmask) + wakeup(&ctlr->tx[i]); +} + +static void +setdma(Desc *d, void *v) +{ + u64int pa = PADDR(v); + REG(d->d[1]) = pa; + REG(d->d[2]) = pa >> 32; +} + +static void +replenish(Desc *d) +{ + d->b = allocb(Rbsz); + cachedwbse(d->b->rp, Rbsz); + setdma(d, d->b->rp); +} + +static int +rxdone(void *arg) +{ + Ring *r = arg; + + r->wp = REG(r->regs[RxWP]) & 0xFFFF; + if(r->rp != r->wp) + return 1; + REG(r->intregs[IntrMaskClr]) = r->intmask; + return 0; +} + +static void +recvproc(void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + Desc *d; + Block *b; + u32int s; + + while(waserror()) + ; + + for(;;){ + if(ctlr->rx->rp == ctlr->rx->wp){ + sleep(ctlr->rx, rxdone, ctlr->rx); + continue; + } + d = &ctlr->rx->d[ctlr->rx->rp & ctlr->rx->m]; + b = d->b; + cachedinvse(b->rp, Rbsz); + s = REG(d->d[0]); + replenish(d); + coherence(); + ctlr->rx->rp = (ctlr->rx->rp + 1) & 0xFFFF; + REG(ctlr->rx->regs[RxRP]) = ctlr->rx->rp; + if((s & (DmaSOP|DmaEOP|DmaRxErrors)) != (DmaSOP|DmaEOP)){ + freeb(b); + continue; + } + b->wp += (s & 0x7FFF0000) >> 16; + etheriq(edev, b, 1); + } +} + +static int +txavail(void *arg) +{ + Ring *r = arg; + + return ((r->wp+1) & r->m) != (r->cp & r->m); +} + +static void +sendproc(void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + Desc *d; + Block *b; + + while(waserror()) + ; + + for(;;){ + if(!txavail(ctlr->tx)){ + sleep(ctlr->avail, txavail, ctlr->tx); + continue; + } + if((b = qbread(edev->oq, 100000)) == nil) + break; + d = &ctlr->tx->d[ctlr->tx->wp & ctlr->tx->m]; + assert(d->b == nil); + d->b = b; + cachedwbse(b->rp, BLEN(b)); + setdma(d, b->rp); + REG(d->d[0]) = BLEN(b)<<16 | DmaTxQtag | DmaSOP | DmaEOP | DmaTxAppendCrc; + coherence(); + ctlr->tx->wp = (ctlr->tx->wp+1) & 0xFFFF; + REG(ctlr->tx->regs[TxWP]) = ctlr->tx->wp; + } +} + +static int +txdone(void *arg) +{ + Ring *r = arg; + + if(r->cp != r->wp){ + r->rp = REG(r->regs[TxRP]) & 0xFFFF; + if(r->cp != r->rp) + return 1; + } + REG(r->intregs[IntrMaskClr]) = r->intmask; + return 0; +} + +static void +freeproc(void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + Desc *d; + + while(waserror()) + ; + + for(;;){ + if(ctlr->tx->cp == ctlr->tx->rp){ + wakeup(ctlr->avail); + sleep(ctlr->tx, txdone, ctlr->tx); + continue; + } + d = &ctlr->tx->d[ctlr->tx->cp & ctlr->tx->m]; + assert(d->b != nil); + freeb(d->b); + d->b = nil; + coherence(); + ctlr->tx->cp = (ctlr->tx->cp+1) & 0xFFFF; + } +} + +static void +initring(Ring *ring, Desc *desc, int start, int size) +{ + ring->d = &desc[start]; + ring->m = size - 1; + ring->cp = ring->rp = ring->wp = 0; + REG(ring->regs[RxWP]) = 0; + REG(ring->regs[RxRP]) = 0; + REG(ring->regs[DmaStart]) = start*3; + REG(ring->regs[DmaEnd]) = (start+size)*3 - 1; + REG(ring->regs[RdmaWP]) = start*3; + REG(ring->regs[RdmaRP]) = start*3; + REG(ring->regs[DmaRingBufSize]) = (size << 16) | Rbsz; + REG(ring->regs[DmaDoneThresh]) = 1; +} + +static void +introff(Ctlr *ctlr) +{ + REG(ctlr->regs[Intrl0 + IntrMaskSet]) = -1; + REG(ctlr->regs[Intrl0 + IntrClr]) = -1; + REG(ctlr->regs[Intrl1 + IntrMaskSet]) = -1; + REG(ctlr->regs[Intrl1 + IntrClr]) = -1; +} + +static void +dmaoff(Ctlr *ctlr) +{ + REG(ctlr->rx->regs[DmaCtrl]) &= ~(RxRingCfgMask<<1 | DmaCtrlEn); + REG(ctlr->tx->regs[DmaCtrl]) &= ~(TxRingCfgMask<<1 | DmaCtrlEn); + + REG(ctlr->regs[UmacTxFlush]) = 1; + microdelay(10); + REG(ctlr->regs[UmacTxFlush]) = 0; + + while((REG(ctlr->rx->regs[DmaStatus]) & DmaStatusDis) == 0) + microdelay(10); + while((REG(ctlr->tx->regs[DmaStatus]) & DmaStatusDis) == 0) + microdelay(10); +} + +static void +dmaon(Ctlr *ctlr) +{ + REG(ctlr->rx->regs[DmaCtrl]) |= DmaCtrlEn; + REG(ctlr->tx->regs[DmaCtrl]) |= DmaCtrlEn; + + while(REG(ctlr->rx->regs[DmaStatus]) & DmaStatusDis) + microdelay(10); + while(REG(ctlr->tx->regs[DmaStatus]) & DmaStatusDis) + microdelay(10); +} + +static void +allocbufs(Ctlr *ctlr) +{ + int i; + + if(scratch == nil){ + scratch = allocb(Rbsz); + memset(scratch->rp, 0xFF, Rbsz); + cachedwbse(scratch->rp, Rbsz); + } + + for(i = 0; i < nelem(ctlr->rd); i++){ + ctlr->rd[i].d = &ctlr->regs[RdmaOffset + i*3]; + replenish(&ctlr->rd[i]); + } + + for(i = 0; i < nelem(ctlr->td); i++){ + ctlr->td[i].d = &ctlr->regs[TdmaOffset + i*3]; + setdma(&ctlr->td[i], scratch->rp); + REG(ctlr->td[i].d[0]) = DmaTxUnderrun; + } +} + +static void +freebufs(Ctlr *ctlr) +{ + int i; + + for(i = 0; i < nelem(ctlr->rd); i++){ + if(ctlr->rd[i].b != nil){ + freeb(ctlr->rd[i].b); + ctlr->rd[i].b = nil; + } + } + for(i = 0; i < nelem(ctlr->td); i++){ + if(ctlr->td[i].b != nil){ + freeb(ctlr->td[i].b); + ctlr->td[i].b = nil; + } + } +} + +static void +initrings(Ctlr *ctlr) +{ + u32int rcfg, tcfg, dmapri[3]; + int i; + + ctlr->rx->intregs = &ctlr->regs[Intrl0]; + ctlr->rx->intmask = IrqRxDmaDone; + ctlr->rx->num = 16; + rcfg = 1<<16; + for(i = 1; i < nelem(ctlr->rx); i++){ + ctlr->rx[i].regs = &ctlr->regs[RdmaOffset + nelem(ctlr->rd)*3 + (i-1)*RingCfg]; + ctlr->rx[i].intregs = &ctlr->regs[Intrl1]; + ctlr->rx[i].intmask = 0x10000 << (i - 1); + ctlr->rx[i].num = i - 1; + rcfg |= 1<<(i-1); + } + assert(rcfg && (rcfg & ~RxRingCfgMask) == 0); + + ctlr->tx->intregs = &ctlr->regs[Intrl0]; + ctlr->tx->intmask = IrqTxDmaDone; + ctlr->tx->num = 16; + tcfg = 1<<16; + for(i = 1; i < nelem(ctlr->tx); i++){ + ctlr->tx[i].regs = &ctlr->regs[TdmaOffset + nelem(ctlr->td)*3 + (i-1)*RingCfg]; + ctlr->tx[i].intregs = &ctlr->regs[Intrl1]; + ctlr->tx[i].intmask = 1 << (i - 1); + ctlr->tx[i].num = i - 1; + tcfg |= 1<<(i-1); + } + assert(tcfg && (tcfg & ~TxRingCfgMask) == 0); + + REG(ctlr->rx->regs[DmaScbBurstSize]) = 0x08; + for(i = 1; i < nelem(ctlr->rx); i++) + initring(&ctlr->rx[i], ctlr->rd, (i-1)*32, 32); + initring(ctlr->rx, ctlr->rd, (i-1)*32, nelem(ctlr->rd) - (i-1)*32); + + for(i = 0; i < nelem(ctlr->rx); i++){ + REG(ctlr->rx[i].regs[DmaDoneThresh]) = 1; + REG(ctlr->rx[i].regs[RdmaXonXoffThresh]) = (5 << 16) | ((ctlr->rx[i].m+1) >> 4); + + // set dma timeout to 50µs + REG(ctlr->rx->regs[RdmaTimeout0 + ctlr->rx[i].num]) = ((50*1000 + 8191)/8192); + } + + REG(ctlr->tx->regs[DmaScbBurstSize]) = 0x08; + for(i = 1; i < nelem(ctlr->tx); i++) + initring(&ctlr->tx[i], ctlr->td, (i-1)*32, 32); + initring(ctlr->tx, ctlr->td, (i-1)*32, nelem(ctlr->td) - (i-1)*32); + + dmapri[0] = dmapri[1] = dmapri[2] = 0; + for(i = 0; i < nelem(ctlr->tx); i++){ + REG(ctlr->tx[i].regs[DmaDoneThresh]) = 10; + REG(ctlr->tx[i].regs[TdmaFlowPeriod]) = i ? 0 : Maxtu << 16; + dmapri[ctlr->tx[i].num/6] |= i << ((ctlr->tx[i].num%6)*5); + } + + REG(ctlr->tx->regs[TdmaArbCtrl]) = 2; + REG(ctlr->tx->regs[TdmaPriority0]) = dmapri[0]; + REG(ctlr->tx->regs[TdmaPriority1]) = dmapri[1]; + REG(ctlr->tx->regs[TdmaPriority2]) = dmapri[2]; + + REG(ctlr->rx->regs[RingCfg]) = rcfg; + REG(ctlr->tx->regs[RingCfg]) = tcfg; + + REG(ctlr->rx->regs[DmaCtrl]) |= rcfg<<1; + REG(ctlr->tx->regs[DmaCtrl]) |= tcfg<<1; +} + +static void +umaccmd(Ctlr *ctlr, u32int set, u32int clr) +{ + ilock(ctlr); + REG(ctlr->regs[UmacCmd]) = (REG(ctlr->regs[UmacCmd]) & ~clr) | set; + iunlock(ctlr); +} + +static void +reset(Ctlr *ctlr) +{ + u32int r; + + // reset umac + r = REG(ctlr->regs[SysRbufFlushCtrl]); + REG(ctlr->regs[SysRbufFlushCtrl]) = r | 2; + microdelay(10); + REG(ctlr->regs[SysRbufFlushCtrl]) = r & ~2; + microdelay(10); + + // umac reset + REG(ctlr->regs[SysRbufFlushCtrl]) = 0; + microdelay(10); + + REG(ctlr->regs[UmacCmd]) = 0; + REG(ctlr->regs[UmacCmd]) = CmdSwReset | CmdLclLoopEn; + microdelay(2); + REG(ctlr->regs[UmacCmd]) = 0; +} + +static void +setmac(Ctlr *ctlr, uchar *ea) +{ + REG(ctlr->regs[UmacMac0]) = ea[0]<<24 | ea[1]<<16 | ea[2]<<8 | ea[3]; + REG(ctlr->regs[UmacMac1]) = ea[4]<<8 | ea[5]; +} + +static void +sethfb(Ctlr *ctlr) +{ + int i; + + REG(ctlr->regs[HfbCtlr]) = 0; + REG(ctlr->regs[HfbFltEnable]) = 0; + REG(ctlr->regs[HfbFltEnable+1]) = 0; + + for(i = 0; i < 8; i++) + REG(ctlr->rx->regs[RdmaIndex2Ring0+i]) = 0; + + for(i = 0; i < 48/4; i++) + REG(ctlr->regs[HfbFltLen + i]) = 0; + + for(i = 0; i < 48*128; i++) + REG(ctlr->regs[HfbOffset + i]) = 0; +} + +static int +mdiodone(void *arg) +{ + Ctlr *ctlr = arg; + REG(ctlr->regs[Intrl0 + IntrMaskClr]) = (IrqMdioDone|IrqMdioError); + return (REG(ctlr->regs[MdioCmd]) & MdioStartBusy) == 0; +} + +static int +mdiowait(Ctlr *ctlr) +{ + REG(ctlr->regs[MdioCmd]) |= MdioStartBusy; + while(REG(ctlr->regs[MdioCmd]) & MdioStartBusy) + tsleep(ctlr->mii, mdiodone, ctlr, 10); + return 0; +} + +static int +mdiow(Mii* mii, int phy, int addr, int data) +{ + Ctlr *ctlr = mii->ctlr; + + if(phy > MdioPhyMask) + return -1; + addr &= MdioAddrMask; + REG(ctlr->regs[MdioCmd]) = MdioWrite + | (phy << MdioPhyShift) | (addr << MdioAddrShift) | (data & 0xFFFF); + return mdiowait(ctlr); +} + +static int +mdior(Mii* mii, int phy, int addr) +{ + Ctlr *ctlr = mii->ctlr; + + if(phy > MdioPhyMask) + return -1; + addr &= MdioAddrMask; + REG(ctlr->regs[MdioCmd]) = MdioRead + | (phy << MdioPhyShift) | (addr << MdioAddrShift); + if(mdiowait(ctlr) < 0) + return -1; + if(REG(ctlr->regs[MdioCmd]) & MdioReadFail) + return -1; + return REG(ctlr->regs[MdioCmd]) & 0xFFFF; +} + +static int +bcmshdr(Mii *mii, int reg) +{ + miimiw(mii, 0x1C, (reg & 0x1F) << 10); + return miimir(mii, 0x1C) & 0x3FF; +} + +static int +bcmshdw(Mii *mii, int reg, int dat) +{ + return miimiw(mii, 0x1C, 0x8000 | (reg & 0x1F) << 10 | (dat & 0x3FF)); +} + +static int +linkevent(void *arg) +{ + Ctlr *ctlr = arg; + REG(ctlr->regs[Intrl0 + IntrMaskClr]) = IrqLinkUp|IrqLinkDown; + return 0; +} + +static void +linkproc(void *arg) +{ + Ether *edev = arg; + Ctlr *ctlr = edev->ctlr; + MiiPhy *phy; + int link = -1; + + while(waserror()) + ; + + for(;;){ + tsleep(ctlr->link, linkevent, ctlr, 1000); + miistatus(ctlr->mii); + phy = ctlr->mii->curphy; + if(phy == nil || phy->link == link) + continue; + link = phy->link; + if(link){ + u32int cmd = CmdRxEn|CmdTxEn; + switch(phy->speed){ + case 1000: cmd |= CmdSpeed1000; break; + case 100: cmd |= CmdSpeed100; break; + case 10: cmd |= CmdSpeed10; break; + } + if(!phy->fd) + cmd |= CmdHdEn; + if(!phy->rfc) + cmd |= CmdRxPauseIgn; + if(!phy->tfc) + cmd |= CmdTxPauseIgn; + + REG(ctlr->regs[ExtRgmiiOobCtrl]) = (REG(ctlr->regs[ExtRgmiiOobCtrl]) & ~OobDisable) | RgmiiLink; + umaccmd(ctlr, cmd, CmdSpeedMask|CmdHdEn|CmdRxPauseIgn|CmdTxPauseIgn); + + edev->mbps = phy->speed; + } + edev->link = link; + // print("#l%d: link %d speed %d\n", edev->ctlrno, edev->link, edev->mbps); + } +} + +static void +setmdfaddr(Ctlr *ctlr, int i, uchar *ea) +{ + REG(ctlr->regs[UmacMdfAddr0 + i*2 + 0]) = ea[0] << 8 | ea[1]; + REG(ctlr->regs[UmacMdfAddr0 + i*2 + 1]) = ea[2] << 24 | ea[3] << 16 | ea[4] << 8 | ea[5]; +} + +static void +rxmode(Ether *edev, int prom) +{ + Ctlr *ctlr = edev->ctlr; + Netaddr *na; + int i; + + if(prom || edev->nmaddr > 16-2){ + REG(ctlr->regs[UmacMdfCtrl]) = 0; + umaccmd(ctlr, CmdProm, 0); + return; + } + setmdfaddr(ctlr, 0, edev->bcast); + setmdfaddr(ctlr, 1, edev->ea); + for(i = 2, na = edev->maddr; na != nil; na = na->next, i++) + setmdfaddr(ctlr, i, na->addr); + REG(ctlr->regs[UmacMdfCtrl]) = (-0x10000 >> i) & 0x1FFFF; + umaccmd(ctlr, 0, CmdProm); +} + +static void +shutdown(Ether *edev) +{ + Ctlr *ctlr = edev->ctlr; + + dmaoff(ctlr); + introff(ctlr); +} + +static void +attach(Ether *edev) +{ + Ctlr *ctlr = edev->ctlr; + + qlock(ctlr); + if(ctlr->attached){ + qunlock(ctlr); + return; + } + if(waserror()){ + print("#l%d: %s\n", edev->ctlrno, up->errstr); + shutdown(edev); + freebufs(ctlr); + qunlock(ctlr); + nexterror(); + } + + // statistics + REG(ctlr->regs[UmacMibCtrl]) = MibResetRx | MibResetTx | MibResetRunt; + REG(ctlr->regs[UmacMibCtrl]) = 0; + + // wol + REG(ctlr->regs[UmacMpdCtrl]) &= ~(MpdPwEn|MpdEn); + + // power + REG(ctlr->regs[UmacEeeCtrl]) &= ~UmacEeeEn; + REG(ctlr->regs[RbufEnergyCtrl]) &= ~(RbufEeeEn|RbufPmEn); + REG(ctlr->regs[TbufEnergyCtrl]) &= ~(RbufEeeEn|RbufPmEn); + REG(ctlr->regs[TbufBpMc]) = 0; + + REG(ctlr->regs[UmacMaxFrameLen]) = Maxtu; + + REG(ctlr->regs[RbufTbufSizeCtrl]) = 1; + + REG(ctlr->regs[TbufCtrl]) &= ~(Rbuf64En); + REG(ctlr->regs[RbufCtrl]) &= ~(Rbuf64En|RbufAlign2B); + REG(ctlr->regs[RbufChkCtrl]) &= ~(RbufChkRxChkEn|RbufChkSkipFcs); + + allocbufs(ctlr); + initrings(ctlr); + dmaon(ctlr); + + setmac(ctlr, edev->ea); + sethfb(ctlr); + rxmode(edev, 0); + + REG(ctlr->regs[SysPortCtrl]) = PortModeExtGphy; + REG(ctlr->regs[ExtRgmiiOobCtrl]) |= RgmiiModeEn | IdModeDis; + + ctlr->mii->ctlr = ctlr; + ctlr->mii->mir = mdior; + ctlr->mii->miw = mdiow; + mii(ctlr->mii, ~0); + + if(ctlr->mii->curphy == nil) + error("no phy"); + + MIIDBG("#l%d: phy%d id %.8ux oui %x\n", + edev->ctlrno, ctlr->mii->curphy->phyno, + ctlr->mii->curphy->id, ctlr->mii->curphy->oui); + + miireset(ctlr->mii); + + switch(ctlr->mii->curphy->id){ + case 0x600d84a2: /* BCM54312PE */ + /* mask interrupts */ + miimiw(ctlr->mii, 0x10, miimir(ctlr->mii, 0x10) | 0x1000); + + /* SCR3: clear DLLAPD_DIS */ + bcmshdw(ctlr->mii, 0x05, bcmshdr(ctlr->mii, 0x05) &~0x0002); + /* APD: set APD_EN */ + bcmshdw(ctlr->mii, 0x0a, bcmshdr(ctlr->mii, 0x0a) | 0x0020); + + /* blinkenlights */ + bcmshdw(ctlr->mii, 0x09, bcmshdr(ctlr->mii, 0x09) | 0x0010); + bcmshdw(ctlr->mii, 0x0d, 3<<0 | 0<<4); + break; + } + + /* don't advertise EEE */ + miimmdw(ctlr->mii, 7, 60, 0); + + miiane(ctlr->mii, ~0, AnaAP|AnaP, ~0); + + ctlr->attached = 1; + + kproc("genet-recv", recvproc, edev); + kproc("genet-send", sendproc, edev); + kproc("genet-free", freeproc, edev); + kproc("genet-link", linkproc, edev); + + qunlock(ctlr); + poperror(); +} + +static void +prom(void *arg, int on) +{ + Ether *edev = arg; + rxmode(edev, on); +} + +static void +multi(void *arg, uchar*, int) +{ + Ether *edev = arg; + rxmode(edev, edev->prom > 0); +} + +static int +pnp(Ether *edev) +{ + static Ctlr ctlr[1]; + + if(ctlr->regs != nil) + return -1; + + ctlr->regs = (u32int*)(VIRTIO + 0x580000); + ctlr->rx->regs = &ctlr->regs[RdmaOffset + nelem(ctlr->rd)*3 + 16*RingCfg]; + ctlr->tx->regs = &ctlr->regs[TdmaOffset + nelem(ctlr->td)*3 + 16*RingCfg]; + + edev->port = (uintptr)ctlr->regs; + edev->irq = IRQether; + edev->ctlr = ctlr; + edev->attach = attach; + edev->shutdown = shutdown; + edev->promiscuous = prom; + edev->multicast = multi; + edev->arg = edev; + edev->mbps = 1000; + edev->maxmtu = Maxtu; + + parseether(edev->ea, getethermac()); + + reset(ctlr); + dmaoff(ctlr); + introff(ctlr); + + intrenable(edev->irq+0, interrupt0, edev, BUSUNKNOWN, edev->name); + intrenable(edev->irq+1, interrupt1, edev, BUSUNKNOWN, edev->name); + + return 0; +} + +void +ethergenetlink(void) +{ + addethercard("genet", pnp); +} diff -Nru /n/sources/plan9/sys/src/9/bcm/ethermii.c /sys/src/9/bcm/ethermii.c --- /n/sources/plan9/sys/src/9/bcm/ethermii.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/ethermii.c Sun Apr 11 00:00:00 2021 @@ -0,0 +1,262 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" +#include "etherif.h" + +#include "ethermii.h" + +int +mii(Mii* mii, int mask) +{ + MiiPhy *miiphy; + int bit, oui, phyno, rmask; + u32int id; + + /* + * Probe through mii for PHYs in mask; + * return the mask of those found in the current probe. + * If the PHY has not already been probed, update + * the Mii information. + */ + rmask = 0; + for(phyno = 0; phyno < NMiiPhy; phyno++){ + bit = 1<mask & bit){ + rmask |= bit; + continue; + } + if(mii->mir(mii, phyno, Bmsr) == -1) + continue; + id = mii->mir(mii, phyno, Phyidr1) << 16; + id |= mii->mir(mii, phyno, Phyidr2); + oui = (id & 0x3FFFFC00)>>10; + if(oui == 0xFFFFF || oui == 0) + continue; + + if((miiphy = malloc(sizeof(MiiPhy))) == nil) + continue; + + miiphy->mii = mii; + miiphy->id = id; + miiphy->oui = oui; + miiphy->phyno = phyno; + + miiphy->anar = ~0; + miiphy->fc = ~0; + miiphy->mscr = ~0; + + mii->phy[phyno] = miiphy; + if(mii->curphy == nil) + mii->curphy = miiphy; + mii->mask |= bit; + mii->nphy++; + + rmask |= bit; + } + return rmask; +} + +int +miimir(Mii* mii, int r) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->mir(mii, mii->curphy->phyno, r); +} + +int +miimiw(Mii* mii, int r, int data) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->miw(mii, mii->curphy->phyno, r, data); +} + +int +miireset(Mii* mii) +{ + int bmcr; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr); + bmcr |= BmcrR; + mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr); + microdelay(1); + + return 0; +} + +int +miiane(Mii* mii, int a, int p, int e) +{ + int anar, bmsr, mscr, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phyno = mii->curphy->phyno; + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrAna)) + return -1; + + if(a != ~0) + anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a; + else if(mii->curphy->anar != ~0) + anar = mii->curphy->anar; + else{ + anar = mii->mir(mii, phyno, Anar); + anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD); + if(bmsr & Bmsr10THD) + anar |= Ana10HD; + if(bmsr & Bmsr10TFD) + anar |= Ana10FD; + if(bmsr & Bmsr100TXHD) + anar |= AnaTXHD; + if(bmsr & Bmsr100TXFD) + anar |= AnaTXFD; + } + mii->curphy->anar = anar; + + if(p != ~0) + anar |= (AnaAP|AnaP) & p; + else if(mii->curphy->fc != ~0) + anar |= mii->curphy->fc; + mii->curphy->fc = (AnaAP|AnaP) & anar; + + if(bmsr & BmsrEs){ + mscr = mii->mir(mii, phyno, Mscr); + mscr &= ~(Mscr1000TFD|Mscr1000THD); + if(e != ~0) + mscr |= (Mscr1000TFD|Mscr1000THD) & e; + else if(mii->curphy->mscr != ~0) + mscr = mii->curphy->mscr; + else{ + r = mii->mir(mii, phyno, Esr); + if(r & Esr1000THD) + mscr |= Mscr1000THD; + if(r & Esr1000TFD) + mscr |= Mscr1000TFD; + } + mii->curphy->mscr = mscr; + mii->miw(mii, phyno, Mscr, mscr); + } + mii->miw(mii, phyno, Anar, anar); + + r = mii->mir(mii, phyno, Bmcr); + if(!(r & BmcrR)){ + r |= BmcrAne|BmcrRan; + mii->miw(mii, phyno, Bmcr, r); + } + + return 0; +} + +int +miistatus(Mii* mii) +{ + MiiPhy *phy; + int anlpar, bmsr, p, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phy = mii->curphy; + phyno = phy->phyno; + + /* + * Check Auto-Negotiation is complete and link is up. + * (Read status twice as the Ls bit is sticky). + */ + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & (BmsrAnc|BmsrAna))) { + // print("miistatus: auto-neg incomplete\n"); + return -1; + } + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrLs)){ + // print("miistatus: link down\n"); + phy->link = 0; + return -1; + } + + phy->speed = phy->fd = phy->rfc = phy->tfc = 0; + if(phy->mscr){ + r = mii->mir(mii, phyno, Mssr); + if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){ + phy->speed = 1000; + phy->fd = 1; + } + else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD)) + phy->speed = 1000; + } + + anlpar = mii->mir(mii, phyno, Anlpar); + if(phy->speed == 0){ + r = phy->anar & anlpar; + if(r & AnaTXFD){ + phy->speed = 100; + phy->fd = 1; + } + else if(r & AnaTXHD) + phy->speed = 100; + else if(r & Ana10FD){ + phy->speed = 10; + phy->fd = 1; + } + else if(r & Ana10HD) + phy->speed = 10; + } + if(phy->speed == 0) { + // print("miistatus: phy speed 0\n"); + return -1; + } + + if(phy->fd){ + p = phy->fc; + r = anlpar & (AnaAP|AnaP); + if(p == AnaAP && r == (AnaAP|AnaP)) + phy->tfc = 1; + else if(p == (AnaAP|AnaP) && r == AnaAP) + phy->rfc = 1; + else if((p & AnaP) && (r & AnaP)) + phy->rfc = phy->tfc = 1; + } + + phy->link = 1; + + return 0; +} + +int +miimmdr(Mii* mii, int a, int r) +{ + a &= 0x1F; + if(miimiw(mii, Mmdctrl, a) == -1) + return -1; + if(miimiw(mii, Mmddata, r) == -1) + return -1; + if(miimiw(mii, Mmdctrl, a | 0x4000) == -1) + return -1; + return miimir(mii, Mmddata); +} + +int +miimmdw(Mii* mii, int a, int r, int data) +{ + a &= 0x1F; + if(miimiw(mii, Mmdctrl, a) == -1) + return -1; + if(miimiw(mii, Mmddata, r) == -1) + return -1; + if(miimiw(mii, Mmdctrl, a | 0x4000) == -1) + return -1; + return miimiw(mii, Mmddata, data); +} diff -Nru /n/sources/plan9/sys/src/9/bcm/ethermii.h /sys/src/9/bcm/ethermii.h --- /n/sources/plan9/sys/src/9/bcm/ethermii.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/ethermii.h Sun Apr 11 00:00:00 2021 @@ -0,0 +1,122 @@ +typedef struct Mii Mii; +typedef struct MiiPhy MiiPhy; + +enum { /* registers */ + Bmcr = 0x00, /* Basic Mode Control */ + Bmsr = 0x01, /* Basic Mode Status */ + Phyidr1 = 0x02, /* PHY Identifier #1 */ + Phyidr2 = 0x03, /* PHY Identifier #2 */ + Anar = 0x04, /* Auto-Negotiation Advertisement */ + Anlpar = 0x05, /* AN Link Partner Ability */ + Aner = 0x06, /* AN Expansion */ + Annptr = 0x07, /* AN Next Page TX */ + Annprr = 0x08, /* AN Next Page RX */ + Mscr = 0x09, /* MASTER-SLAVE Control */ + Mssr = 0x0A, /* MASTER-SLAVE Status */ + Mmdctrl = 0x0D, /* MMD Access Control */ + Mmddata = 0x0E, /* MMD Access Data Register */ + Esr = 0x0F, /* Extended Status */ + + NMiiPhyr = 32, + NMiiPhy = 32, +}; + +enum { /* Bmcr */ + BmcrSs1 = 0x0040, /* Speed Select[1] */ + BmcrCte = 0x0080, /* Collision Test Enable */ + BmcrDm = 0x0100, /* Duplex Mode */ + BmcrRan = 0x0200, /* Restart Auto-Negotiation */ + BmcrI = 0x0400, /* Isolate */ + BmcrPd = 0x0800, /* Power Down */ + BmcrAne = 0x1000, /* Auto-Negotiation Enable */ + BmcrSs0 = 0x2000, /* Speed Select[0] */ + BmcrLe = 0x4000, /* Loopback Enable */ + BmcrR = 0x8000, /* Reset */ +}; + +enum { /* Bmsr */ + BmsrEc = 0x0001, /* Extended Capability */ + BmsrJd = 0x0002, /* Jabber Detect */ + BmsrLs = 0x0004, /* Link Status */ + BmsrAna = 0x0008, /* Auto-Negotiation Ability */ + BmsrRf = 0x0010, /* Remote Fault */ + BmsrAnc = 0x0020, /* Auto-Negotiation Complete */ + BmsrPs = 0x0040, /* Preamble Suppression Capable */ + BmsrEs = 0x0100, /* Extended Status */ + Bmsr100T2HD = 0x0200, /* 100BASE-T2 HD Capable */ + Bmsr100T2FD = 0x0400, /* 100BASE-T2 FD Capable */ + Bmsr10THD = 0x0800, /* 10BASE-T HD Capable */ + Bmsr10TFD = 0x1000, /* 10BASE-T FD Capable */ + Bmsr100TXHD = 0x2000, /* 100BASE-TX HD Capable */ + Bmsr100TXFD = 0x4000, /* 100BASE-TX FD Capable */ + Bmsr100T4 = 0x8000, /* 100BASE-T4 Capable */ +}; + +enum { /* Anar/Anlpar */ + Ana10HD = 0x0020, /* Advertise 10BASE-T */ + Ana10FD = 0x0040, /* Advertise 10BASE-T FD */ + AnaTXHD = 0x0080, /* Advertise 100BASE-TX */ + AnaTXFD = 0x0100, /* Advertise 100BASE-TX FD */ + AnaT4 = 0x0200, /* Advertise 100BASE-T4 */ + AnaP = 0x0400, /* Pause */ + AnaAP = 0x0800, /* Asymmetrical Pause */ + AnaRf = 0x2000, /* Remote Fault */ + AnaAck = 0x4000, /* Acknowledge */ + AnaNp = 0x8000, /* Next Page Indication */ +}; + +enum { /* Mscr */ + Mscr1000THD = 0x0100, /* Advertise 1000BASE-T HD */ + Mscr1000TFD = 0x0200, /* Advertise 1000BASE-T FD */ +}; + +enum { /* Mssr */ + Mssr1000THD = 0x0400, /* Link Partner 1000BASE-T HD able */ + Mssr1000TFD = 0x0800, /* Link Partner 1000BASE-T FD able */ +}; + +enum { /* Esr */ + Esr1000THD = 0x1000, /* 1000BASE-T HD Capable */ + Esr1000TFD = 0x2000, /* 1000BASE-T FD Capable */ + Esr1000XHD = 0x4000, /* 1000BASE-X HD Capable */ + Esr1000XFD = 0x8000, /* 1000BASE-X FD Capable */ +}; + +typedef struct Mii { + Lock; + int nphy; + int mask; + MiiPhy* phy[NMiiPhy]; + MiiPhy* curphy; + + void* ctlr; + int (*mir)(Mii*, int, int); + int (*miw)(Mii*, int, int, int); +} Mii; + +typedef struct MiiPhy { + Mii* mii; + u32int id; + int oui; + int phyno; + + int anar; + int fc; + int mscr; + + int link; + int speed; + int fd; + int rfc; + int tfc; +}; + +extern int mii(Mii*, int); +extern int miiane(Mii*, int, int, int); +extern int miimir(Mii*, int); +extern int miimiw(Mii*, int, int); +extern int miireset(Mii*); +extern int miistatus(Mii*); + +extern int miimmdr(Mii*, int, int); +extern int miimmdw(Mii*, int, int, int); 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 Sun Apr 11 00:00:00 2021 @@ -8,6 +8,7 @@ #include "dat.h" #include "fns.h" #include "io.h" +#include "ureg.h" #include "../port/error.h" #include "../port/netif.h" @@ -29,6 +30,8 @@ SmscRxerror = 0x8000, SmscTxfirst = 0x2000, SmscTxlast = 0x1000, + Lan78Rxerror = 0x00400000, + Lan78Txfcs = 1<<22, }; typedef struct Ctlr Ctlr; @@ -63,16 +66,19 @@ { Unbind, "unbind", 0, }, }; -static Unpackfn unpackcdc, unpackasix, unpacksmsc; -static Transmitfn transmitcdc, transmitasix, transmitsmsc; +static Unpackfn unpackcdc, unpackasix, unpacksmsc, unpacklan78; +static Transmitfn transmitcdc, transmitasix, transmitsmsc, transmitlan78; static Udev udevtab[] = { { "cdc", unpackcdc, transmitcdc, }, { "asix", unpackasix, transmitasix, }, { "smsc", unpacksmsc, transmitsmsc, }, + { "lan78xx", unpacklan78, transmitlan78, }, { nil }, }; +static char nullea[Eaddrlen]; + static void dump(int c, Block *b) { @@ -175,6 +181,33 @@ return 0; } +static int +unpacklan78(Ether *edev, Block *b) +{ + ulong hd; + int m; + + if(BLEN(b) < 10) + return -1; + hd = GET4(b->rp); + b->rp += 10; + m = hd & 0x3FFF; + if(m < 6 || m > BLEN(b)) + return -1; + if(hd & Lan78Rxerror){ + edev->frames++; + b->rp += m; + if(BLEN(b) == 0){ + freeb(b); + return 1; + } + }else if(unpack(edev, b, m) == 1) + return 1; + if(BLEN(b) > 0) + b->rp = (uchar*)((((uintptr)b->rp)+3)&~3); + return 0; +} + static void transmit(Ctlr *ctlr, Block *b) { @@ -198,10 +231,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,13 +247,25 @@ 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); } static void +transmitlan78(Ctlr *ctlr, Block *b) +{ + int n; + + n = BLEN(b) & 0xFFFFF; + b = padblock(b, 8); + PUT4(b->rp, n | Lan78Txfcs); + PUT4(b->rp+4, n); + transmit(ctlr, b); +} + +static void etherusbproc(void *a) { Ether *edev; @@ -263,6 +308,7 @@ Chan *inchan, *outchan; char *buf; uint bufsize, maxpkt; + uchar ea[Eaddrlen]; qlock(ctlr); inchan = outchan = nil; @@ -288,10 +334,12 @@ 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); - print("\netherusb %s: %E\n", udev->name, ctlr->edev->addr); + 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"); ctlr->buf = buf; ctlr->inchan = inchan; ctlr->outchan = outchan; @@ -299,6 +347,8 @@ ctlr->maxpkt = maxpkt; ctlr->udev = udev; kproc("etherusb", etherusbproc, ctlr->edev); + memmove(ctlr->edev->addr, ea, Eaddrlen); + print("\netherusb %s: %E\n", udev->name, ctlr->edev->addr); poperror(); qunlock(ctlr); } @@ -397,12 +447,32 @@ } 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; ctlr = edev->ctlr; - ctlr->edev = edev; + if(ctlr->edev == 0){ + /* + * Don't let boot process access etherusb until + * usbether driver has assigned an address. + */ + if(up->pid == 1 && strcmp(up->text, "boot") == 0) + while(memcmp(edev->ea, nullea, Eaddrlen) == 0) + tsleep(&up->sleep, return0, 0, 100); + ctlr->edev = edev; + } } static int @@ -427,8 +497,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 Sun Apr 11 00:00:00 2021 @@ -5,75 +5,115 @@ 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 ulong cprd(int cp, int op1, int crn, int crm, int op2); -extern ulong cprdsc(int op1, int crn, int crm, int op2); +extern u32int cpidget(void); +extern u32int cprdcpaccess(void); +extern u32int cprdfeat1(void); extern void cpuidprint(void); -extern void cpwr(int cp, int op1, int crn, int crm, int op2, ulong val); -extern void cpwrsc(int op1, int crn, int crm, int op2, ulong val); +extern char *cputype2name(char *buf, int size); +extern void cpwrcpaccess(u32int); +extern void cpwrtimerphysctl(u32int); +extern void cpwrtimerphysval(u32int); #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); extern void* fbinit(int, int*, int*, int*); extern u32int farget(void); extern void fpon(void); -extern ulong fprd(int fpreg); +extern ulong fprdexc(void); +extern ulong fprdscr(void); +extern ulong fprdsid(void); +extern void fpwrexc(ulong); +extern void fpwrscr(ulong); extern void fprestreg(int fpreg, uvlong val); +extern void fprestregs(uvlong*, int); extern void fpsave(FPsave *); extern ulong fpsavereg(int fpreg, uvlong *fpp); -extern void fpwr(int fpreg, ulong val); +extern void fpsaveregs(uvlong*, int); 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(uint); +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); -extern uintptr mmukmap(uintptr, uintptr, usize); extern void okay(int); extern void procrestore(Proc *); extern void procsave(Proc*); 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 sev(void); +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); +extern int xhcireset(int devaddr); /* * floating point emulation @@ -90,6 +130,19 @@ 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 mmukmapx(uintptr, uvlong, 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 +167,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 Sun Apr 11 00:00:00 2021 @@ -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/gisb.c /sys/src/9/bcm/gisb.c --- /n/sources/plan9/sys/src/9/bcm/gisb.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/gisb.c Sun Apr 11 00:00:00 2021 @@ -0,0 +1,90 @@ +/* + * from 9front + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "ureg.h" + +/* + * GISB arbiter registers + */ +static u32int *regs = (u32int*)(VIRTIO + 0x400000); + +enum { + ArbMasterMask = 0x004/4, + ArbTimer = 0x008/4, + TimerFreq = 216000000, // 216MHz + + ArbErrCapClear = 0x7e4/4, + ArbErrCapAddrHi = 0x7e8/4, + ArbErrCapAddr = 0x7ec/4, + ArbErrCapData = 0x7f0/4, + ArbErrCapStatus = 0x7f4/4, + CapStatusTimeout = 1<<12, + CapStatusAbort = 1<<11, + CapStatusStrobe = 15<<2, + CapStatusWrite = 1<<1, + CapStatusValid = 1<<0, + ArbErrCapMaster = 0x7f8/4, + + ArbIntrSts = 0x3000/4, + ArbIntrSet = 0x3004/4, + ArbIntrClr = 0x3008/4, + + ArbCpuMaskSet = 0x3010/4, +}; + +static int +arberror(Ureg*) +{ + u32int status, intr; + u32int master, data; + uvlong addr; + + status = regs[ArbErrCapStatus]; + if((status & CapStatusValid) == 0) + return 0; + intr = regs[ArbIntrSts]; + master = regs[ArbErrCapMaster]; + addr = regs[ArbErrCapAddr]; + addr |= (uvlong)regs[ArbErrCapAddrHi]<<32; + data = regs[ArbErrCapData]; + if(intr) + regs[ArbIntrClr] = intr; + regs[ArbErrCapClear] = CapStatusValid; + + iprint("cpu%d: GISB arbiter error: %s%s %s bus addr %llux data %.8ux, " + "master %.8ux, status %.8ux, intr %.8ux\n", + m->machno, + (status & CapStatusTimeout) ? "timeout" : "", + (status & CapStatusAbort) ? "abort" : "", + (status & CapStatusWrite) ? "writing" : "reading", + addr, data, + master, status, intr); + + return 1; +} + +static void +arbclock(void) +{ + arberror(nil); +} + +void +gisblink(void) +{ + extern int (*buserror)(Ureg*); // trap.c + + regs[ArbErrCapClear] = CapStatusValid; + regs[ArbIntrClr] = -1; + + addclock0link(arbclock, 100); + + //buserror = arberror; +} 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 Sun Apr 11 00:00:00 2021 @@ -0,0 +1,111 @@ +/* + * 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, + /* BCM2711 only */ + PUPPDN0 = 0xe4>>2, + PUPPDN1 = 0xe8>>2, + PUPPDN2 = 0xec>>2, + PUPPDN3 = 0xf0>>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 Sun Apr 11 00:00:00 2021 @@ -8,11 +8,26 @@ IRQdma0 = 16, #define IRQDMA(chan) (IRQdma0+(chan)) IRQaux = 29, + IRQi2c = 53, + IRQspi = 54, + IRQsdhost = 56, IRQmmc = 62, IRQbasic = 64, IRQtimerArm = IRQbasic + 0, + IRQpci = 84, + IRQether = 93, + + 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,7 +35,14 @@ DmaM2M = 2, /* memory to memory */ DmaChanEmmc = 4, /* can only use 2-5, maybe 0 */ + DmaChanSdhost = 5, + DmaChanSpiTx= 2, + DmaChanSpiRx= 0, + + DmaDevSpiTx = 6, + DmaDevSpiRx = 7, DmaDevEmmc = 11, + DmaDevSdhost = 13, PowerSd = 0, PowerUart0, 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 Sun Apr 11 00:00:00 2021 @@ -1,10 +1,14 @@ /* - * Broadcom bcm2835 SoC, as used in Raspberry Pi - * arm1176jzf-s processor (armv6) + * Common startup and coprocessor instructions 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,144 @@ MOVW $0, R0 /* - * SVC mode, interrupts disabled - */ - MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 - MOVW R1, CPSR - - /* - * disable the mmu and L1 caches - * invalidate caches and tlb - */ - MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl - BIC $(CpCdcache|CpCicache|CpCpredict|CpCmmu), R1 - MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl - MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvu), CpCACHEall - MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv - ISB - - /* - * clear mach and page tables - */ - MOVW $PADDR(MACHADDR), R1 - MOVW $PADDR(KTZERO), R2 -_ramZ: - MOVW R0, (R1) - ADD $4, R1 - CMP R1, R2 - BNE _ramZ - - /* * start stack at top of mach (physical addr) - * set up page tables for kernel */ MOVW $PADDR(MACHADDR+MACHSIZE-4), R13 - BL ,mmuinit(SB) - - /* - * set up domain access control and page table base - */ - MOVW $Client, R1 - MCR CpSC, 0, R1, C(CpDAC), C(0) - MOVW $PADDR(L1), R1 - MCR CpSC, 0, R1, C(CpTTB), C(0) - - /* - * enable caches, mmu, and high vectors - */ - MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl - ORR $(CpChv|CpCdcache|CpCicache|CpCmmu), R0 - MCR CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl - ISB - - /* - * switch SB, SP, and PC into KZERO space - */ - MOVW $setR12(SB), R12 - MOVW $(MACHADDR+MACHSIZE-4), R13 - MOVW $_startpg(SB), R15 - -TEXT _startpg(SB), 1, $-4 - - /* - * enable cycle counter - */ - MOVW $1, R1 - MCR CpSC, 0, R1, C(CpSPM), C(CpSPMperf), CpSPMctl /* - * call main and loop forever if it returns + * do arch-dependent startup (no return) */ - 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 + * coprocessor instructions for local timer (armv7) */ -TEXT mmuinvalidate(SB), 1, $-4 - MOVW $0, R0 - MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv - BARRIERS +TEXT cprdfeat1(SB), 1, $-4 + MRC CpSC, 0, R0, C(CpID), C(CpIDfeat), 1 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 +TEXT cpwrtimerphysctl(SB), 1, $-4 + MCR CpSC, 0, R0, C(CpTIMER), C(CpTIMERphys), CpTIMERphysctl 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 +TEXT cpwrtimerphysval(SB), 1, $-4 + MCR CpSC, 0, R0, C(CpTIMER), C(CpTIMERphys), CpTIMERphysval RET /* - * cachedwbinvse(va, n) - * drain write buffer - * writeback and invalidate data cache range [va, va+n) + * coprocessor instructions for vfp3 */ -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 +#define VSTR(fpreg,Rn,off) \ + WORD $(0xed800000 | (fpreg&15)<<12 | ((fpreg&16)<<(22-4)) | Rn<<16 | CpDFP<<8 | (off)) +#define VLDR(fpreg,Rn,off) \ + WORD $(0xed900000 | (fpreg&15)<<12 | ((fpreg&16)<<(22-4)) | Rn<<16 | CpDFP<<8 | (off)) +#define VMSR(fpctlreg,Rt) WORD $(0xeee00010 | fpctlreg<<16 | Rt<<12 | CpFP<<8) +#define VMRS(Rt,fpctlreg) WORD $(0xeef00010 | fpctlreg<<16 | Rt<<12 | CpFP<<8) +#define Fpsid 0 +#define Fpscr 1 +#define Fpexc 8 -/* - * drain write buffer and prefetch buffer - * writeback and invalidate data cache - * invalidate instruction cache - */ -TEXT cacheuwbinv(SB), 1, $-4 +TEXT cprdcpaccess(SB), 1, $-4 + MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpCPaccess + RET +TEXT cpwrcpaccess(SB), 1, $-4 + MCR CpSC, 0, R0, C(CpCONTROL), C(0), CpCPaccess 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 +TEXT fprdsid(SB), 1, $-4 + VMRS(0,Fpsid) + RET +TEXT fprdscr(SB), 1, $-4 + VMRS(0,Fpscr) + RET +TEXT fprdexc(SB), 1, $-4 + VMRS(0,Fpexc) + RET +TEXT fpwrscr(SB), 1, $-4 + VMSR(Fpscr,0) + RET +TEXT fpwrexc(SB), 1, $-4 + VMSR(Fpexc,0) + RET + +TEXT fpsaveregs(SB), 1, $-4 + MOVW R0, R1 /* dest */ + MOVW 4(FP), R2 /* reg count */ + VSTR(0, 1, 0*2) + VSTR(1, 1, 1*2) + VSTR(2, 1, 2*2) + VSTR(3, 1, 3*2) + VSTR(4, 1, 4*2) + VSTR(5, 1, 5*2) + VSTR(6, 1, 6*2) + VSTR(7, 1, 7*2) + VSTR(8, 1, 8*2) + VSTR(9, 1, 9*2) + VSTR(10, 1, 10*2) + VSTR(11, 1, 11*2) + VSTR(12, 1, 12*2) + VSTR(13, 1, 13*2) + VSTR(14, 1, 14*2) + VSTR(15, 1, 15*2) + CMP $16, R2 + BEQ fpsavex + VSTR(16, 1, 16*2) + VSTR(17, 1, 17*2) + VSTR(18, 1, 18*2) + VSTR(19, 1, 19*2) + VSTR(20, 1, 20*2) + VSTR(21, 1, 21*2) + VSTR(22, 1, 22*2) + VSTR(23, 1, 23*2) + VSTR(24, 1, 24*2) + VSTR(25, 1, 25*2) + VSTR(26, 1, 26*2) + VSTR(27, 1, 27*2) + VSTR(28, 1, 28*2) + VSTR(29, 1, 29*2) + VSTR(30, 1, 30*2) + VSTR(31, 1, 31*2) +fpsavex: + RET + +TEXT fprestregs(SB), 1, $-4 + MOVW R0, R1 /* src */ + MOVW 4(FP), R2 /* reg count */ + VLDR(0, 1, 0*2) + VLDR(1, 1, 1*2) + VLDR(2, 1, 2*2) + VLDR(3, 1, 3*2) + VLDR(4, 1, 4*2) + VLDR(5, 1, 5*2) + VLDR(6, 1, 6*2) + VLDR(7, 1, 7*2) + VLDR(8, 1, 8*2) + VLDR(9, 1, 9*2) + VLDR(10, 1, 10*2) + VLDR(11, 1, 11*2) + VLDR(12, 1, 12*2) + VLDR(13, 1, 13*2) + VLDR(14, 1, 14*2) + VLDR(15, 1, 15*2) + CMP $16, R2 + BEQ fprestx + VLDR(16, 1, 16*2) + VLDR(17, 1, 17*2) + VLDR(18, 1, 18*2) + VLDR(19, 1, 19*2) + VLDR(20, 1, 20*2) + VLDR(21, 1, 21*2) + VLDR(22, 1, 22*2) + VLDR(23, 1, 23*2) + VLDR(24, 1, 24*2) + VLDR(25, 1, 25*2) + VLDR(26, 1, 26*2) + VLDR(27, 1, 27*2) + VLDR(28, 1, 28*2) + VLDR(29, 1, 29*2) + VLDR(30, 1, 30*2) + VLDR(31, 1, 31*2) +fprestx: 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 Sun Apr 11 00:00:00 2021 @@ -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 Sun Apr 11 00:00:00 2021 @@ -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; + + m->machno = 0; + machaddr[m->machno] = m; - active.machs = 1; + 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) + print("only %d cpu%s started\n", 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(); @@ -257,7 +330,10 @@ if(conf.monitor) swcursorinit(); cpuidprint(); + print("clocks: CPU %lud core %lud UART %lud EMMC %lud\n", + getclkrate(ClkArm), getclkrate(ClkCore), getclkrate(ClkUart), getclkrate(ClkEmmc)); archreset(); + vgpinit(); procinit0(); initseg(); @@ -266,6 +342,9 @@ pageinit(); swapinit(); userinit(); + launchinit(getncpus()); + mmuinit1(); + schedinit(); assert(0); /* shouldn't have returned */ } @@ -277,6 +356,7 @@ init0(void) { int i; + Chan *c; char buf[2*KNAMELEN]; up->nerrlab = 0; @@ -310,6 +390,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); @@ -418,7 +506,7 @@ void confinit(void) { - int i; + int i, userpcnt; ulong kpages; uintptr pa; char *p; @@ -439,8 +527,40 @@ if(conf.mem[0].limit == 0){ conf.mem[0].base = PHYSDRAM; conf.mem[0].limit = PHYSDRAM + memsize; - }else if(p != nil) - conf.mem[0].limit = conf.mem[0].base + memsize; + } + /* + * pi4 extra memory (beyond video ram) indicated by board id + */ + switch(getboardrev()&0xF00000){ + case 0xA00000: + break; + case 0xB00000: + conf.mem[1].base = 1*GiB; + conf.mem[1].limit = 2*GiB; + break; + case 0xC00000: + conf.mem[1].base = 1*GiB; + conf.mem[1].limit = 0xFFF00000; + break; + case 0xD00000: + conf.mem[1].base = 1*GiB; + conf.mem[1].limit = 0xFFF00000; + break; + } + if(conf.mem[1].limit > soc.dramsize) + conf.mem[1].limit = soc.dramsize; + if(p != nil){ + if(memsize < conf.mem[0].limit){ + conf.mem[0].limit = memsize; + conf.mem[1].limit = 0; + }else if(memsize >= conf.mem[1].base && memsize < conf.mem[1].limit) + conf.mem[1].limit = memsize; + } + + if(p = getconf("*kernelpercent")) + userpcnt = 100 - strtol(p, 0, 0); + else + userpcnt = 0; conf.npage = 0; pa = PADDR(PGROUND(PTR2UINT(end))); @@ -458,12 +578,13 @@ conf.npage += conf.mem[i].npage; } - conf.upages = (conf.npage*80)/100; + if(userpcnt < 10 || userpcnt > 99) + userpcnt = 90; + conf.upages = (conf.npage*userpcnt)/100; + if(conf.npage - conf.upages > 256*MiB/BY2PG) + conf.upages = conf.npage - 256*MiB/BY2PG; 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 +595,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 +635,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 +666,21 @@ void exit(int code) { + void (*f)(ulong, ulong, ulong); + shutdown(code); splfhi(); - archreboot(); + if(m->machno == 0) + archreboot(); + else{ + f = (void*)REBOOTADDR; + intrcpushutdown(); + memmove(f, rebootcode, sizeof(rebootcode)); + cachedwbse(f, sizeof(rebootcode)); + cacheiinvse(f, sizeof(rebootcode)); + (*f)(0, soc.armlocal, 0); + for(;;){} + } } /* @@ -542,9 +689,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 +717,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 +752,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 Sun Apr 11 00:00:00 2021 @@ -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 2GB dram */ +#define KSEGM 0x80000000 #define KZERO KSEG0 /* kernel address space */ #define CONFADDR (KZERO+0x100) /* unparsed plan9.ini */ #define MACHADDR (KZERO+0x2000) /* Mach structure */ @@ -38,24 +46,28 @@ #define FIQSTKTOP (KZERO+0x4000) /* FIQ stack */ #define L1 (KZERO+0x4000) /* tt ptes: 16KiB aligned */ #define KTZERO (KZERO+0x8000) /* kernel text start */ -#define VIRTIO 0x7E000000 /* i/o registers */ -#define FRAMEBUFFER 0xA0000000 /* video framebuffer */ +#define VIRTPCI 0xFD000000 /* pcie address space (pi4 only) */ +#define VIRTIO 0xFE000000 /* i/o registers */ +#define IOSIZE (10*MiB) +#define ARMLOCAL (VIRTIO+IOSIZE) /* armv7 only */ +#define VGPIO (ARMLOCAL+MiB) /* virtual gpio for pi3 ACT LED */ +#define FRAMEBUFFER (VGPIO+MiB) /* 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 +78,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 +99,3 @@ * 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 Sun Apr 11 00:00:00 2021 @@ -1,5 +1,5 @@ -CONF=pi -CONFLIST=pi picpu pifat +CONF=pi2 +CONFLIST=pi picpu pifat pi2 pi2cpu pi4 pi4cpu piwifi pi2wifi pi4wifi EXTRACOPIES= loadaddr=0x80008000 @@ -8,7 +8,7 @@ soc.dramsize) + pe = soc.dramsize; + for(pa = PHYSDRAM; pa < PHYSDRAM+pe; pa += MiB){ + l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|L1ptedramattrs; va += MiB; } /* * identity map first MB of ram so mmu can be enabled */ - l1[L1X(PHYSDRAM)] = PHYSDRAM|Dom0|L1AP(Krw)|Section|Cached|Buffered; + l1[L1X(PHYSDRAM)] = PHYSDRAM|Dom0|L1AP(Krw)|Section|L1ptedramattrs; /* * map i/o registers */ va = VIRTIO; - for(pa = PHYSIO; pa < PHYSIO+IOSIZE; pa += MiB){ - l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section; + for(pa = soc.physio; pa < soc.physio+IOSIZE; pa += MiB){ + l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|L1noexec; va += MiB; } - + pa = soc.armlocal; + if(pa) + l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|L1noexec; /* - * double map exception vectors at top of virtual memory + * pi4 hack: ether and pcie are in segment 0xFD5xxxxx not 0xFE5xxxxx + * gisb is in segment 0xFC4xxxxx not FE4xxxxx + */ + va = VIRTIO + 0x500000; + pa = soc.physio - 0x1000000 + 0x500000; + l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|L1noexec; + va = VIRTIO + 0x400000; + pa = soc.physio - 0x2000000 + 0x400000; + l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|L1noexec; + + /* + * double map exception vectors near top of virtual memory */ va = HVECTORS; l1[L1X(va)] = (uintptr)l2|Dom0|Coarse; - l2[L2X(va)] = PHYSDRAM|L2AP(Krw)|Small; + l2[L2X(va)] = PHYSDRAM|L2AP(Krw)|Small|L2ptedramattrs; } void -mmuinit1(void) +mmuinit1() { - PTE *l1; + PTE *l1, *l2; + uintptr va; - l1 = (PTE*)L1; - m->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); + + /* + * make a local mapping for highest MB of virtual space + * containing kmap area and exception vectors + */ + if(m->machno == 0) + m->kmapl2 = (PTE*)L2; + else{ + va = HVECTORS; + m->kmapl2 = l2 = mallocalign(L2size, L2size, 0, 0); + l1[L1X(va)] = PADDR(l2)|Dom0|Coarse; + l2[L2X(va)] = PHYSDRAM|L2AP(Krw)|Small|L2ptedramattrs; + cachedwbtlb(&l1[L1X(va)], sizeof(PTE)); + mmuinvalidateaddr(va); + } } static void @@ -75,15 +116,20 @@ { PTE *l1; Page **l2, *page; + KMap *k; l1 = m->mmul1; l2 = &proc->mmul2; for(page = *l2; page != nil; page = page->next){ - if(clear) - memset(UINT2PTR(page->va), 0, BY2PG); + if(clear){ + k = kmap(page); + memset((void*)VA(k), 0, L2size); + kunmap(k); + } l1[page->daddr] = Fault; l2 = &page->next; } + coherence(); *l2 = proc->mmul2cache; proc->mmul2cache = proc->mmul2; proc->mmul2 = nil; @@ -92,29 +138,26 @@ 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 */ + if(m->kmapl2 != nil) + memset(m->kmapl2, 0, NKMAPS*sizeof(PTE)); } void @@ -124,15 +167,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 +176,26 @@ /* move in new map */ l1 = m->mmul1; - for(page = proc->mmul2; page != nil; page = page->next){ + 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; + } + } + if(proc->nkmap) + memmove(m->kmapl2, proc->kmaptab, sizeof(proc->kmaptab)); } /* 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)); + if(proc != nil && proc->nkmap) + cachedwbtlb(m->kmapl2, sizeof(proc->kmaptab)); /* lose any possible stale tlb entries */ mmuinvalidate(); @@ -175,9 +217,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 +232,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,42 +241,52 @@ void putmmu(uintptr va, uintptr pa, Page* page) { - int x; + int x, s; Page *pg; PTE *l1, *pte; + KMap *k; + /* + * 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 */ - pg = newpage(1, 0, 0); - pg->va = VA(kmap(pg)); - } - else{ + spllo(); + pg = newpage(0, 0, 0); + splhi(); + /* if newpage slept, we might be on a different cpu */ + l1 = &m->mmul1[x]; + }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); + k = kmap(pg); + memset((void*)VA(k), 0, L2size); + cachedwbtlb((void*)VA(k), L2size); + kunmap(k); *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))); + k = kmapp(PPN(*l1)); + pte = (PTE*)VA(k); /* protection bits are * PTERONLY|PTEVALID; @@ -246,30 +295,53 @@ */ 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)); + kunmap(k); /* 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; + if(cankaddr(page->pa)) + 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; } /* @@ -279,8 +351,8 @@ uintptr cankaddr(uintptr pa) { - if(pa < PHYSDRAM + memsize) /* assumes PHYSDRAM is 0 */ - return PHYSDRAM + memsize - pa; + if((pa - PHYSDRAM) < VIRTPCI-KZERO) + return PHYSDRAM + VIRTPCI-KZERO - pa; return 0; } @@ -301,18 +373,108 @@ return 0; pte = pte0; for(n = 0; n < size; n += MiB){ - *pte++ = (pa+n)|Dom0|L1AP(Krw)|Section; + *pte++ = (pa+n)|Dom0|L1AP(Krw)|Section|L1noexec; mmuinvalidateaddr(va+n); } - cachedwbse(pte0, pte - pte0); + cachedwbtlb(pte0, (uintptr)pte - (uintptr)pte0); return va + o; } +uintptr +mmukmapx(uintptr va, uvlong pa, usize size) +{ + int o; + usize n; + PTE ptex, *pte, *pte0; + + assert((va & (16*MiB-1)) == 0); + assert(size <= 16*MiB); + o = (int)pa & (16*MiB-1); + pa -= o; + ptex = FEXT(pa,24,8)<<24 | FEXT(pa,32,4)<<20 | FEXT(pa,36,4)<<5; + pte = pte0 = &m->mmul1[L1X(va)]; + for(n = 0; n < 16*MiB; n += MiB) + if(*pte++ != Fault) + return 0; + pte = pte0; + for(n = 0; n < 16*MiB; n += MiB) + *pte++ = ptex|L1AP(Krw)|Super|Section|L1noexec; + mmuinvalidateaddr(va); + cachedwbtlb(pte0, (uintptr)pte - (uintptr)pte0); + return va + o; +} void checkmmu(uintptr va, uintptr pa) { - USED(va); - USED(pa); + int x; + PTE *l1, *pte; + KMap *k; + + 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; + } + k = kmapp(PPN(*l1)); + pte = (PTE*)VA(k); + 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); + kunmap(k); +} + +static KMap* +kmapp(ulong pa) +{ + int s, i; + uintptr va; + + if(cankaddr(pa)) + return KADDR(pa); + if(up == nil) + panic("kmap without up %#p", getcallerpc(&pa)); + s = splhi(); + if(up->nkmap == NKMAPS) + panic("kmap overflow %#p", getcallerpc(&pa)); + for(i = 0; i < NKMAPS; i++) + if(up->kmaptab[i] == 0) + break; + if(i == NKMAPS) + panic("can't happen"); + up->nkmap++; + va = KMAPADDR + i*BY2PG; + up->kmaptab[i] = pa | L2AP(Krw)|Small|L2ptedramattrs; + m->kmapl2[i] = up->kmaptab[i]; + cachedwbtlb(&m->kmapl2[i], sizeof(PTE)); + mmuinvalidateaddr(va); + splx(s); + return (KMap*)va; +} + +KMap* +kmap(Page *p) +{ + return kmapp(p->pa); } +void +kunmap(KMap *k) +{ + int i; + uintptr va; + + coherence(); + va = (uintptr)k; + if(L1X(va) != L1X(KMAPADDR)) + return; + /* wasteful: only needed for text pages aliased within data cache */ + cachedwbse((void*)PPN(va), BY2PG); + i = L2X(va); + up->kmaptab[i] = 0; + m->kmapl2[i] = 0; + up->nkmap--; + cachedwbtlb(&m->kmapl2[i], sizeof(PTE)); + mmuinvalidateaddr(va); +} diff -Nru /n/sources/plan9/sys/src/9/bcm/pci.c /sys/src/9/bcm/pci.c --- /n/sources/plan9/sys/src/9/bcm/pci.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/pci.c Sun Apr 11 00:00:00 2021 @@ -0,0 +1,1136 @@ +/* + * from 9front + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "pci.h" + +#define PCIWIN 0x0600000000ULL + +/* bcmstb PCIe controller registers */ +enum{ + RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1 = 0x0188/4, + RC_CFG_PRIV1_ID_VAL3 = 0x043c/4, + RC_DL_MDIO_ADDR = 0x1100/4, + RC_DL_MDIO_WR_DATA = 0x1104/4, + RC_DL_MDIO_RD_DATA = 0x1108/4, + MISC_MISC_CTRL = 0x4008/4, + MISC_CPU_2_PCIE_MEM_WIN0_LO = 0x400c/4, + MISC_CPU_2_PCIE_MEM_WIN0_HI = 0x4010/4, + MISC_RC_BAR1_CONFIG_LO = 0x402c/4, + MISC_RC_BAR2_CONFIG_LO = 0x4034/4, + MISC_RC_BAR2_CONFIG_HI = 0x4038/4, + MISC_RC_BAR3_CONFIG_LO = 0x403c/4, + MISC_MSI_BAR_CONFIG_LO = 0x4044/4, + MISC_MSI_BAR_CONFIG_HI = 0x4048/4, + MISC_MSI_DATA_CONFIG = 0x404c/4, + MISC_EOI_CTRL = 0x4060/4, + MISC_PCIE_CTRL = 0x4064/4, + MISC_PCIE_STATUS = 0x4068/4, + MISC_REVISION = 0x406c/4, + MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT = 0x4070/4, + MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI = 0x4080/4, + MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI = 0x4084/4, + MISC_HARD_PCIE_HARD_DEBUG = 0x4204/4, + + INTR2_CPU_BASE = 0x4300/4, + MSI_INTR2_BASE = 0x4500/4, + INTR_STATUS = 0, + INTR_SET, + INTR_CLR, + INTR_MASK_STATUS, + INTR_MASK_SET, + INTR_MASK_CLR, + + EXT_CFG_INDEX = 0x9000/4, + RGR1_SW_INIT_1 = 0x9210/4, + EXT_CFG_DATA = 0x8000/4, + +}; + +#define MSI_TARGET_ADDR 0xFFFFFFFFCULL + +static u32int *regs = (u32int*)(VIRTIO + 0x500000); + +static Lock pcicfglock; +static int pcimaxbno = 0; +static int pcimaxdno = 0; +static Pcidev* pciroot; +static Pcidev* pcilist; +static Pcidev* pcitail; + +typedef struct Pcisiz Pcisiz; +struct Pcisiz +{ + Pcidev* dev; + int siz; + int bar; +}; + +enum +{ + MaxFNO = 7, + MaxUBN = 255, +}; + +static char* bustypes[] = { + "CBUSI", + "CBUSII", + "EISA", + "FUTURE", + "INTERN", + "ISA", + "MBI", + "MBII", + "MCA", + "MPI", + "MPSA", + "NUBUS", + "PCI", + "PCMCIA", + "TC", + "VL", + "VME", + "XPRESS", +}; + +static int +tbdffmt(Fmt* fmt) +{ + char *p; + int l, r; + uint type, tbdf; + + if((p = malloc(READSTR)) == nil) + return fmtstrcpy(fmt, "(tbdfconv)"); + + switch(fmt->r){ + case 'T': + tbdf = va_arg(fmt->args, int); + if(tbdf == BUSUNKNOWN) + snprint(p, READSTR, "unknown"); + else{ + type = BUSTYPE(tbdf); + if(type < nelem(bustypes)) + l = snprint(p, READSTR, bustypes[type]); + else + l = snprint(p, READSTR, "%d", type); + snprint(p+l, READSTR-l, ".%d.%d.%d", + BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + } + break; + + default: + snprint(p, READSTR, "(tbdfconv)"); + break; + } + r = fmtstrcpy(fmt, p); + free(p); + + return r; +} + +static void pcicfginit(void); + +static void* +cfgaddr(int tbdf, int rno) +{ + if(BUSBNO(tbdf) == 0 && BUSDNO(tbdf) == 0) + return (uchar*)regs + rno; + regs[EXT_CFG_INDEX] = BUSBNO(tbdf) << 20 | BUSDNO(tbdf) << 15 | BUSFNO(tbdf) << 12; + coherence(); + return ((uchar*)®s[EXT_CFG_DATA]) + rno; +} + +static int +pcicfgrw32(int tbdf, int rno, int data, int read) +{ + int x = -1; + u32int *p; + + ilock(&pcicfglock); + if((p = cfgaddr(tbdf, rno & ~3)) != nil){ + if(read) + x = *p; + else + *p = data; + } + iunlock(&pcicfglock); + return x; +} +static int +pcicfgrw16(int tbdf, int rno, int data, int read) +{ + int x = -1; + u16int *p; + + ilock(&pcicfglock); + if((p = cfgaddr(tbdf, rno & ~1)) != nil){ + if(read) + x = *p; + else + *p = data; + } + iunlock(&pcicfglock); + return x; +} +static int +pcicfgrw8(int tbdf, int rno, int data, int read) +{ + int x = -1; + u8int *p; + + ilock(&pcicfglock); + if((p = cfgaddr(tbdf, rno)) != nil){ + if(read) + x = *p; + else + *p = data; + } + iunlock(&pcicfglock); + return x; +} + +int +pcicfgr32(Pcidev* pcidev, int rno) +{ + return pcicfgrw32(pcidev->tbdf, rno, 0, 1); +} +void +pcicfgw32(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw32(pcidev->tbdf, rno, data, 0); +} +int +pcicfgr16(Pcidev* pcidev, int rno) +{ + return pcicfgrw16(pcidev->tbdf, rno, 0, 1); +} +void +pcicfgw16(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw16(pcidev->tbdf, rno, data, 0); +} +int +pcicfgr8(Pcidev* pcidev, int rno) +{ + return pcicfgrw8(pcidev->tbdf, rno, 0, 1); +} +void +pcicfgw8(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw8(pcidev->tbdf, rno, data, 0); +} + +Pcidev* +pcimatch(Pcidev* prev, int vid, int did) +{ + if(prev == nil) + prev = pcilist; + else + prev = prev->list; + + while(prev != nil){ + if((vid == 0 || prev->vid == vid) + && (did == 0 || prev->did == did)) + break; + prev = prev->list; + } + return prev; +} + +Pcidev* +pcimatchtbdf(int tbdf) +{ + Pcidev *pcidev; + + for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) { + if(pcidev->tbdf == tbdf) + break; + } + return pcidev; +} + +static u32int +pcibarsize(Pcidev *p, int rno) +{ + u32int v, size; + + v = pcicfgrw32(p->tbdf, rno, 0, 1); + pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0); + size = pcicfgrw32(p->tbdf, rno, 0, 1); + if(v & 1) + size |= 0xFFFF0000; + pcicfgrw32(p->tbdf, rno, v, 0); + + return -(size & ~0x0F); +} + +static int +pcisizcmp(void *a, void *b) +{ + Pcisiz *aa, *bb; + + aa = a; + bb = b; + return aa->siz - bb->siz; +} + +static ulong +pcimask(ulong v) +{ + ulong m; + + m = BI2BY*sizeof(v); + for(m = 1<<(m-1); m != 0; m >>= 1) { + if(m & v) + break; + } + + m--; + if((v & m) == 0) + return v; + + v |= m; + return v+1; +} + +static void +pcibusmap(Pcidev *root, uintpci *pmema, uintpci *pioa, int wrreg) +{ + Pcidev *p; + int ntb, i, size, rno, hole; + uintpci v, mema, ioa, sioa, smema, base, limit; + Pcisiz *table, *tptr, *mtb, *itb; + + ioa = *pioa; + mema = *pmema; + + ntb = 0; + for(p = root; p != nil; p = p->link) + ntb++; + + ntb *= (PciCIS-PciBAR0)/4; + table = malloc(2*ntb*sizeof(Pcisiz)); + if(table == nil) + panic("pcibusmap: can't allocate memory"); + itb = table; + mtb = table+ntb; + + /* + * Build a table of sizes + */ + for(p = root; p != nil; p = p->link) { + if(p->ccrb == 0x06) { + if(p->ccru != 0x04 || p->bridge == nil) + continue; + + sioa = ioa; + smema = mema; + pcibusmap(p->bridge, &smema, &sioa, 0); + + hole = pcimask(smema-mema); + if(hole < (1<<20)) + hole = 1<<20; + p->mema.size = hole; + + hole = pcimask(sioa-ioa); + if(hole < (1<<12)) + hole = 1<<12; + + p->ioa.size = hole; + + itb->dev = p; + itb->bar = -1; + itb->siz = p->ioa.size; + itb++; + + mtb->dev = p; + mtb->bar = -1; + mtb->siz = p->mema.size; + mtb++; + continue; + } + + for(i = 0; i <= 5; i++) { + rno = PciBAR0 + i*4; + v = pcicfgrw32(p->tbdf, rno, 0, 1); + size = pcibarsize(p, rno); + if(size == 0) + continue; + + p->mem[i].size = size; + if(v & 1) { + itb->dev = p; + itb->bar = i; + itb->siz = size; + itb++; + } + else { + mtb->dev = p; + mtb->bar = i; + mtb->siz = size; + mtb++; + + if((v & 7) == 4) + i++; + } + } + } + + /* + * Sort both tables IO smallest first, Memory largest + */ + qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp); + tptr = table+ntb; + qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp); + + /* + * Allocate IO address space on this bus + */ + for(tptr = table; tptr < itb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<12; + ioa = (ioa+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->ioa.bar = ioa; + else { + p->pcr |= IOen; + p->mem[tptr->bar].bar = ioa|1; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0); + } + + ioa += tptr->siz; + } + + /* + * Allocate Memory address space on this bus + */ + for(tptr = table+ntb; tptr < mtb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<20; + mema = (mema+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->mema.bar = mema; + else { + p->pcr |= MEMen; + p->mem[tptr->bar].bar = mema; + if(wrreg){ + rno = PciBAR0+(tptr->bar*4); + if((mema >> 32) != 0){ + pcicfgrw32(p->tbdf, rno, mema|4, 0); + pcicfgrw32(p->tbdf, rno+4, mema >> 32, 0); + } else { + pcicfgrw32(p->tbdf, rno, mema, 0); + } + } + } + mema += tptr->siz; + } + + *pmema = mema; + *pioa = ioa; + free(table); + + if(wrreg == 0) + return; + + /* + * Finally set all the bridge addresses & registers + */ + for(p = root; p != nil; p = p->link) { + if(p->bridge == nil) { + if(p->cls == 0){ + p->cls = 64; + pcicfgw8(p, PciCLS, p->cls); + } + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + p->pcr |= MASen; + pcicfgrw16(p->tbdf, PciPCR, p->pcr, 0); + continue; + } + + if(p == pciroot){ + base = p->mema.bar; + limit = base+p->mema.size-1; + regs[MISC_CPU_2_PCIE_MEM_WIN0_LO] = base; + regs[MISC_CPU_2_PCIE_MEM_WIN0_HI] = base >> 32; + base >>= 20, limit >>= 20; + regs[MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT] = (base & 0xFFF) << 4 | (limit & 0xFFF) << 20; + regs[MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI] = base >> 12; + regs[MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI] = limit >> 12; + } + + base = p->ioa.bar; + limit = base+p->ioa.size-1; + v = pcicfgrw32(p->tbdf, PciIBR, 0, 1); + v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8); + pcicfgrw32(p->tbdf, PciIBR, v, 0); + v = (limit & 0xFFFF0000)|(base>>16); + pcicfgrw32(p->tbdf, PciIUBR, v, 0); + + base = p->mema.bar; + limit = base+p->mema.size-1; + v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16); + pcicfgrw32(p->tbdf, PciMBR, v, 0); + + /* + * Disable memory prefetch + */ + pcicfgrw32(p->tbdf, PciPMBR, 0x0000FFFF, 0); + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + /* + * Enable the bridge + */ + p->pcr |= IOen|MEMen|MASen; + pcicfgrw32(p->tbdf, PciPCR, 0xFFFF0000|p->pcr, 0); + + sioa = p->ioa.bar; + smema = p->mema.bar; + pcibusmap(p->bridge, &smema, &sioa, 1); + } +} + +static int +pcilscan(int bno, Pcidev** list, Pcidev *parent) +{ + Pcidev *p, *head, *tail; + int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn; + + maxubn = bno; + head = nil; + tail = nil; + for(dno = 0; dno <= pcimaxdno; dno++){ + maxfno = 0; + for(fno = 0; fno <= maxfno; fno++){ + /* + * For this possible device, form the + * bus+device+function triplet needed to address it + * and try to read the vendor and device ID. + * If successful, allocate a device struct and + * start to fill it in with some useful information + * from the device's configuration space. + */ + tbdf = MKBUS(BusPCI, bno, dno, fno); + l = pcicfgrw32(tbdf, PciVID, 0, 1); + if(l == 0xFFFFFFFF || l == 0) + continue; + p = malloc(sizeof(*p)); + if(p == nil) + panic("pcilscan: no memory"); + p->tbdf = tbdf; + p->vid = l; + p->did = l>>16; + + if(pcilist != nil) + pcitail->list = p; + else + pcilist = p; + pcitail = p; + + p->pcr = pcicfgr16(p, PciPCR); + p->rid = pcicfgr8(p, PciRID); + p->ccrp = pcicfgr8(p, PciCCRp); + p->ccru = pcicfgr8(p, PciCCRu); + p->ccrb = pcicfgr8(p, PciCCRb); + p->cls = pcicfgr8(p, PciCLS); + p->ltr = pcicfgr8(p, PciLTR); + + p->intl = pcicfgr8(p, PciINTL); + + /* + * If the device is a multi-function device adjust the + * loop count so all possible functions are checked. + */ + hdt = pcicfgr8(p, PciHDT); + if(hdt & 0x80) + maxfno = MaxFNO; + + /* + * If appropriate, read the base address registers + * and work out the sizes. + */ + switch(p->ccrb) { + case 0x00: /* prehistoric */ + case 0x01: /* mass storage controller */ + case 0x02: /* network controller */ + case 0x03: /* display controller */ + case 0x04: /* multimedia device */ + case 0x07: /* simple comm. controllers */ + case 0x08: /* base system peripherals */ + case 0x09: /* input devices */ + case 0x0A: /* docking stations */ + case 0x0B: /* processors */ + case 0x0C: /* serial bus controllers */ + case 0x0D: /* wireless controllers */ + case 0x0E: /* intelligent I/O controllers */ + case 0x0F: /* sattelite communication controllers */ + case 0x10: /* encryption/decryption controllers */ + case 0x11: /* signal processing controllers */ + if((hdt & 0x7F) != 0) + break; + rno = PciBAR0; + for(i = 0; i <= 5; i++) { + p->mem[i].bar = pcicfgr32(p, rno); + p->mem[i].size = pcibarsize(p, rno); + if((p->mem[i].bar & 7) == 4 && i < 5){ + rno += 4; + p->mem[i].bar |= (uintpci)pcicfgr32(p, rno) << 32; + i++; + } + rno += 4; + } + break; + + case 0x05: /* memory controller */ + case 0x06: /* bridge device */ + default: + break; + } + + p->parent = parent; + if(head != nil) + tail->link = p; + else + head = p; + tail = p; + } + } + + *list = head; + for(p = head; p != nil; p = p->link){ + /* + * Find PCI-PCI bridges and recursively descend the tree. + */ + if(p->ccrb != 0x06 || p->ccru != 0x04) + continue; + + /* + * If the secondary or subordinate bus number is not + * initialised try to do what the PCI BIOS should have + * done and fill in the numbers as the tree is descended. + * On the way down the subordinate bus number is set to + * the maximum as it's not known how many buses are behind + * this one; the final value is set on the way back up. + */ + sbn = pcicfgr8(p, PciSBN); + ubn = pcicfgr8(p, PciUBN); + + if(sbn == 0 || ubn == 0) { + sbn = maxubn+1; + /* + * Make sure memory, I/O and master enables are + * off, set the primary, secondary and subordinate + * bus numbers and clear the secondary status before + * attempting to scan the secondary bus. + * + * Initialisation of the bridge should be done here. + */ + pcicfgw32(p, PciPCR, 0xFFFF0000); + l = (MaxUBN<<16)|(sbn<<8)|bno; + pcicfgw32(p, PciPBN, l); + pcicfgw16(p, PciSPSR, 0xFFFF); + maxubn = pcilscan(sbn, &p->bridge, p); + l = (maxubn<<16)|(sbn<<8)|bno; + + pcicfgw32(p, PciPBN, l); + } + else { + if(ubn > maxubn) + maxubn = ubn; + pcilscan(sbn, &p->bridge, p); + } + } + + return maxubn; +} + +static void +pcicfginit(void) +{ + uintpci mema, ioa; + + fmtinstall('T', tbdffmt); + + pcilscan(0, &pciroot, nil); + + /* + * Work out how big the top bus is + */ + ioa = 0; + mema = 0; + pcibusmap(pciroot, &mema, &ioa, 0); + + /* + * Align the windows and map it + */ + ioa = 0; + mema = PCIWIN; + pcibusmap(pciroot, &mema, &ioa, 1); +} + +static void +pcilhinv(Pcidev* p) +{ + int i; + Pcidev *t; + + if(p == nil) { + p = pciroot; + print("pci dev type vid did intl memory\n"); + } + for(t = p; t != nil; t = t->link) { + print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ", + BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf), + t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl); + + for(i = 0; i < nelem(p->mem); i++) { + if(t->mem[i].size == 0) + continue; + print("%d:%llux %d ", i, + (uvlong)t->mem[i].bar, t->mem[i].size); + } + if(t->bridge) + print("->%d", BUSBNO(t->bridge->tbdf)); + print("\n"); + } + while(p != nil) { + if(p->bridge != nil) + pcilhinv(p->bridge); + p = p->link; + } +} + +static void +pcihinv(Pcidev* p) +{ + pcilhinv(p); +} + +void +pcisetioe(Pcidev* p) +{ + p->pcr |= IOen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pciclrioe(Pcidev* p) +{ + p->pcr &= ~IOen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pcisetbme(Pcidev* p) +{ + p->pcr |= MASen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pciclrbme(Pcidev* p) +{ + p->pcr &= ~MASen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pcisetmwi(Pcidev* p) +{ + p->pcr |= MemWrInv; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pciclrmwi(Pcidev* p) +{ + p->pcr &= ~MemWrInv; + pcicfgw16(p, PciPCR, p->pcr); +} + +static int +enumcaps(Pcidev *p, int (*fmatch)(Pcidev*, int, int, int), int arg) +{ + int i, r, cap, off; + + /* status register bit 4 has capabilities */ + if((pcicfgr16(p, PciPSR) & 1<<4) == 0) + return -1; + switch(pcicfgr8(p, PciHDT) & 0x7F){ + default: + return -1; + case 0: /* etc */ + case 1: /* pci to pci bridge */ + off = 0x34; + break; + case 2: /* cardbus bridge */ + off = 0x14; + break; + } + for(i = 48; i--;){ + off = pcicfgr8(p, off); + if(off < 0x40 || (off & 3)) + break; + off &= ~3; + cap = pcicfgr8(p, off); + if(cap == 0xff) + break; + r = (*fmatch)(p, cap, off, arg); + if(r < 0) + break; + if(r == 0) + return off; + off++; + } + return -1; +} + +static int +matchcap(Pcidev *, int cap, int, int arg) +{ + return cap != arg; +} + +static int +matchhtcap(Pcidev *p, int cap, int off, int arg) +{ + int mask; + + if(cap != PciCapHTC) + return 1; + if(arg == 0x00 || arg == 0x20) + mask = 0xE0; + else + mask = 0xF8; + cap = pcicfgr8(p, off+3); + return (cap & mask) != arg; +} + +int +pcicap(Pcidev *p, int cap) +{ + return enumcaps(p, matchcap, cap); +} + +int +pcinextcap(Pcidev *pci, int offset) +{ + if(offset == 0) { + if((pcicfgr16(pci, PciPSR) & (1<<4)) == 0) + return 0; /* no capabilities */ + offset = PciCAP-1; + } + return pcicfgr8(pci, offset+1) & ~3; +} + +int +pcihtcap(Pcidev *p, int cap) +{ + return enumcaps(p, matchhtcap, cap); +} + +static int +pcigetpmrb(Pcidev* p) +{ + if(p->pmrb != 0) + return p->pmrb; + return p->pmrb = pcicap(p, PciCapPMG); +} + +int +pcigetpms(Pcidev* p) +{ + int pmcsr, ptr; + + if((ptr = pcigetpmrb(p)) == -1) + return -1; + + /* + * Power Management Register Block: + * offset 0: Capability ID + * 1: next item pointer + * 2: capabilities + * 4: control/status + * 6: bridge support extensions + * 7: data + */ + pmcsr = pcicfgr16(p, ptr+4); + + return pmcsr & 0x0003; +} + +int +pcisetpms(Pcidev* p, int state) +{ + int ostate, pmc, pmcsr, ptr; + + if((ptr = pcigetpmrb(p)) == -1) + return -1; + + pmc = pcicfgr16(p, ptr+2); + pmcsr = pcicfgr16(p, ptr+4); + ostate = pmcsr & 0x0003; + pmcsr &= ~0x0003; + + switch(state){ + default: + return -1; + case 0: + break; + case 1: + if(!(pmc & 0x0200)) + return -1; + break; + case 2: + if(!(pmc & 0x0400)) + return -1; + break; + case 3: + break; + } + pmcsr |= state; + pcicfgw16(p, ptr+4, pmcsr); + + return ostate; +} + +void +pcienable(Pcidev *p) +{ + uint pcr; + int i; + + if(p == nil) + return; + + pcienable(p->parent); + + switch(pcisetpms(p, 0)){ + case 1: + print("pcienable %T: wakeup from D1\n", p->tbdf); + break; + case 2: + print("pcienable %T: wakeup from D2\n", p->tbdf); + if(p->bridge != nil) + delay(100); /* B2: minimum delay 50ms */ + else + delay(1); /* D2: minimum delay 200µs */ + break; + case 3: + print("pcienable %T: wakeup from D3\n", p->tbdf); + delay(100); /* D3: minimum delay 50ms */ + + /* restore registers */ + for(i = 0; i < 6; i++) + pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar); + pcicfgw8(p, PciINTL, p->intl); + pcicfgw8(p, PciLTR, p->ltr); + pcicfgw8(p, PciCLS, p->cls); + pcicfgw16(p, PciPCR, p->pcr); + break; + } + + if(p->bridge != nil) + pcr = IOen|MEMen|MASen; + else { + pcr = 0; + for(i = 0; i < 6; i++){ + if(p->mem[i].size == 0) + continue; + if(p->mem[i].bar & 1) + pcr |= IOen; + else + pcr |= MEMen; + } + } + + if((p->pcr & pcr) != pcr){ + print("pcienable %T: pcr %ux->%ux\n", p->tbdf, p->pcr, p->pcr|pcr); + p->pcr |= pcr; + pcicfgrw32(p->tbdf, PciPCR, 0xFFFF0000|p->pcr, 0); + } +} + +void +pcidisable(Pcidev *p) +{ + if(p == nil) + return; + pciclrbme(p); +} + +enum { + MSICtrl = 0x02, /* message control register (16 bit) */ + MSIAddr = 0x04, /* message address register (64 bit) */ + MSIData32 = 0x08, /* message data register for 32 bit MSI (16 bit) */ + MSIData64 = 0x0C, /* message data register for 64 bit MSI (16 bit) */ +}; + +typedef struct Pciisr Pciisr; +struct Pciisr { + void (*f)(Ureg*, void*); + void *a; + Pcidev *p; +}; + +static Pciisr pciisr[32]; +static Lock pciisrlk; + +void +pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a) +{ + int cap, ok64; + u32int dat; + u64int adr; + Pcidev *p; + Pciisr *isr; + + if((p = pcimatchtbdf(tbdf)) == nil){ + print("pciintrenable: %T: unknown device\n", tbdf); + return; + } + if((cap = pcicap(p, PciCapMSI)) < 0){ + print("pciintrenable: %T: no MSI cap\n", tbdf); + return; + } + + lock(&pciisrlk); + for(isr = pciisr; isr < &pciisr[nelem(pciisr)]; isr++){ + if(isr->p == p){ + isr->p = nil; + regs[MSI_INTR2_BASE + INTR_MASK_SET] = 1 << (isr-pciisr); + break; + } + } + for(isr = pciisr; isr < &pciisr[nelem(pciisr)]; isr++){ + if(isr->p == nil){ + isr->p = p; + isr->a = a; + isr->f = f; + regs[MSI_INTR2_BASE + INTR_CLR] = 1 << (isr-pciisr); + regs[MSI_INTR2_BASE + INTR_MASK_CLR] = 1 << (isr-pciisr); + break; + } + } + unlock(&pciisrlk); + + if(isr >= &pciisr[nelem(pciisr)]){ + print("pciintrenable: %T: out of isr slots\n", tbdf); + return; + } + + adr = MSI_TARGET_ADDR; + ok64 = (pcicfgr16(p, cap + MSICtrl) & (1<<7)) != 0; + pcicfgw32(p, cap + MSIAddr, adr); + if(ok64) pcicfgw32(p, cap + MSIAddr + 4, adr>>32); + dat = regs[MISC_MSI_DATA_CONFIG]; + dat = ((dat >> 16) & (dat & 0xFFFF)) | (isr-pciisr); + pcicfgw16(p, cap + (ok64 ? MSIData64 : MSIData32), dat); + pcicfgw16(p, cap + MSICtrl, 1); +} + +void +pciintrdisable(int tbdf, void (*f)(Ureg*, void*), void *a) +{ + Pciisr *isr; + + lock(&pciisrlk); + for(isr = pciisr; isr < &pciisr[nelem(pciisr)]; isr++){ + if(isr->p != nil && isr->p->tbdf == tbdf && isr->f == f && isr->a == a){ + regs[MSI_INTR2_BASE + INTR_MASK_SET] = 1 << (isr-pciisr); + isr->p = nil; + isr->f = nil; + isr->a = nil; + break; + } + } + unlock(&pciisrlk); +} + +static void +pciinterrupt(Ureg *ureg, void*) +{ + Pciisr *isr; + u32int sts; + + sts = regs[MSI_INTR2_BASE + INTR_STATUS]; + if(sts == 0) + return; + regs[MSI_INTR2_BASE + INTR_CLR] = sts; + for(isr = pciisr; sts != 0 && isr < &pciisr[nelem(pciisr)]; isr++, sts>>=1){ + if((sts & 1) != 0 && isr->f != nil) + (*isr->f)(ureg, isr->a); + } + regs[MISC_EOI_CTRL] = 1; +} + +void +pcilink(void) +{ + int log2dmasize = 30; // 1GB + + regs[RGR1_SW_INIT_1] |= 3; + delay(200); + regs[RGR1_SW_INIT_1] &= ~2; + regs[MISC_PCIE_CTRL] &= ~5; + delay(200); + + regs[MISC_HARD_PCIE_HARD_DEBUG] &= ~0x08000000; + delay(200); + + regs[MSI_INTR2_BASE + INTR_CLR] = -1; + regs[MSI_INTR2_BASE + INTR_MASK_SET] = -1; + + regs[MISC_CPU_2_PCIE_MEM_WIN0_LO] = 0; + regs[MISC_CPU_2_PCIE_MEM_WIN0_HI] = 0; + regs[MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT] = 0; + regs[MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI] = 0; + regs[MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI] = 0; + + // SCB_ACCESS_EN, CFG_READ_UR_MODE, MAX_BURST_SIZE_128, SCB0SIZE + regs[MISC_MISC_CTRL] = 1<<12 | 1<<13 | 0<<20 | (log2dmasize-15)<<27; + + regs[MISC_RC_BAR2_CONFIG_LO] = (log2dmasize-15); + regs[MISC_RC_BAR2_CONFIG_HI] = 0; + + regs[MISC_RC_BAR1_CONFIG_LO] = 0; + regs[MISC_RC_BAR3_CONFIG_LO] = 0; + + regs[MISC_MSI_BAR_CONFIG_LO] = MSI_TARGET_ADDR | 1; + regs[MISC_MSI_BAR_CONFIG_HI] = MSI_TARGET_ADDR>>32; + regs[MISC_MSI_DATA_CONFIG] = 0xFFF86540; + intrenable(IRQpci, pciinterrupt, nil, BUSUNKNOWN, "pci"); + + // force to GEN2 + regs[(0xAC + 12)/4] = (regs[(0xAC + 12)/4] & ~15) | 2; // linkcap + regs[(0xAC + 48)/4] = (regs[(0xAC + 48)/4] & ~15) | 2; // linkctl2 + + regs[RGR1_SW_INIT_1] &= ~1; + delay(500); + + if((regs[MISC_PCIE_STATUS] & 0x30) != 0x30){ + print("pcireset: phy link is down\n"); + return; + } + + regs[RC_CFG_PRIV1_ID_VAL3] = 0x060400; + regs[RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1] &= ~0xC; + regs[MISC_HARD_PCIE_HARD_DEBUG] |= 2; + + pcicfginit(); + pcihinv(nil); +} diff -Nru /n/sources/plan9/sys/src/9/bcm/pci.h /sys/src/9/bcm/pci.h --- /n/sources/plan9/sys/src/9/bcm/pci.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/pci.h Sun Apr 11 00:00:00 2021 @@ -0,0 +1,241 @@ +/* + * PCI + */ +typedef unsigned long long uintpci; + +#define BUSUNKNOWN (-1) + +enum { + BusCBUS = 0, /* Corollary CBUS */ + BusCBUSII, /* Corollary CBUS II */ + BusEISA, /* Extended ISA */ + BusFUTURE, /* IEEE Futurebus */ + BusINTERN, /* Internal bus */ + BusISA, /* Industry Standard Architecture */ + BusMBI, /* Multibus I */ + BusMBII, /* Multibus II */ + BusMCA, /* Micro Channel Architecture */ + BusMPI, /* MPI */ + BusMPSA, /* MPSA */ + BusNUBUS, /* Apple Macintosh NuBus */ + BusPCI, /* Peripheral Component Interconnect */ + BusPCMCIA, /* PC Memory Card International Association */ + BusTC, /* DEC TurboChannel */ + BusVL, /* VESA Local bus */ + BusVME, /* VMEbus */ + BusXPRESS, /* Express System Bus */ +}; + +#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) +#define BUSFNO(tbdf) (((tbdf)>>8)&0x07) +#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) +#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) +#define BUSTYPE(tbdf) ((tbdf)>>24) +#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) + +enum { /* type 0 & type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciCAP = 0x34, /* capabilities pointer */ + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +/* ccrb (base class code) values; controller types */ +enum { + Pcibcpci1 = 0, /* pci 1.0; no class codes defined */ + Pcibcstore = 1, /* mass storage */ + Pcibcnet = 2, /* network */ + Pcibcdisp = 3, /* display */ + Pcibcmmedia = 4, /* multimedia */ + Pcibcmem = 5, /* memory */ + Pcibcbridge = 6, /* bridge */ + Pcibccomm = 7, /* simple comms (e.g., serial) */ + Pcibcbasesys = 8, /* base system */ + Pcibcinput = 9, /* input */ + Pcibcdock = 0xa, /* docking stations */ + Pcibcproc = 0xb, /* processors */ + Pcibcserial = 0xc, /* serial bus (e.g., USB) */ + Pcibcwireless = 0xd, /* wireless */ + Pcibcintell = 0xe, /* intelligent i/o */ + Pcibcsatcom = 0xf, /* satellite comms */ + Pcibccrypto = 0x10, /* encryption/decryption */ + Pcibcdacq = 0x11, /* data acquisition & signal proc. */ +}; + +/* ccru (sub-class code) values; common cases only */ +enum { + /* mass storage */ + Pciscscsi = 0, /* SCSI */ + Pciscide = 1, /* IDE (ATA) */ + Pciscsata = 6, /* SATA */ + + /* network */ + Pciscether = 0, /* Ethernet */ + + /* display */ + Pciscvga = 0, /* VGA */ + Pciscxga = 1, /* XGA */ + Pcisc3d = 2, /* 3D */ + + /* bridges */ + Pcischostpci = 0, /* host/pci */ + Pciscpcicpci = 1, /* pci/pci */ + + /* simple comms */ + Pciscserial = 0, /* 16450, etc. */ + Pciscmultiser = 1, /* multiport serial */ + + /* serial bus */ + Pciscusb = 3, /* USB */ +}; + +enum { /* type 0 pre-defined header */ + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* subsystem ID */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +enum { /* type 2 pre-defined header */ + PciCBExCA = 0x10, + PciCBSPSR = 0x16, + PciCBPBN = 0x18, /* primary bus number */ + PciCBSBN = 0x19, /* secondary bus number */ + PciCBUBN = 0x1A, /* subordinate bus number */ + PciCBSLTR = 0x1B, /* secondary latency timer */ + PciCBMBR0 = 0x1C, + PciCBMLR0 = 0x20, + PciCBMBR1 = 0x24, + PciCBMLR1 = 0x28, + PciCBIBR0 = 0x2C, /* I/O base */ + PciCBILR0 = 0x30, /* I/O limit */ + PciCBIBR1 = 0x34, /* I/O base */ + PciCBILR1 = 0x38, /* I/O limit */ + PciCBSVID = 0x40, /* subsystem vendor ID */ + PciCBSID = 0x42, /* subsystem ID */ + PciCBLMBAR = 0x44, /* legacy mode base address */ +}; + +enum { + /* bar bits */ + Barioaddr = 1<<0, /* vs. memory addr */ + Barwidthshift = 1, + Barwidthmask = 3, + Barwidth32 = 0, + Barwidth64 = 2, + Barprefetch = 1<<3, +}; + +enum +{ /* command register */ + IOen = (1<<0), + MEMen = (1<<1), + MASen = (1<<2), + MemWrInv = (1<<4), + PErrEn = (1<<6), + SErrEn = (1<<8), +}; + +/* capabilities */ +enum { + PciCapPMG = 0x01, /* power management */ + PciCapAGP = 0x02, + PciCapVPD = 0x03, /* vital product data */ + PciCapSID = 0x04, /* slot id */ + PciCapMSI = 0x05, + PciCapCHS = 0x06, /* compact pci hot swap */ + PciCapPCIX = 0x07, + PciCapHTC = 0x08, /* hypertransport irq conf */ + PciCapVND = 0x09, /* vendor specific information */ + PciCapPCIe = 0x10, + PciCapMSIX = 0x11, + PciCapSATA = 0x12, + PciCapHSW = 0x0c, /* hot swap */ +}; + +typedef struct Pcidev Pcidev; +struct Pcidev +{ + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + ushort pcr; + + uchar rid; + uchar ccrp; + uchar ccru; + uchar ccrb; + uchar cls; + uchar ltr; + + struct { + uintpci bar; /* base address */ + int size; + } mem[6]; + + uchar intl; /* interrupt line */ + + Pcidev* list; + Pcidev* link; /* next device on this bno */ + + Pcidev* parent; /* up a bus */ + Pcidev* bridge; /* down a bus */ + + int pmrb; /* power management register block */ + + struct { + uintpci bar; + int size; + } ioa, mema; +}; + +#define PCIWINDOW 0 +#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) + +#pragma varargck type "T" int +#pragma varargck type "T" uint + +void pcienable(Pcidev *); +void pcidisable(Pcidev *); +void pcisetbme(Pcidev* ); +Pcidev* pcimatch(Pcidev* prev, int vid, int did); +void pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a); +void pciintrdisable(int tbdf, void (*f)(Ureg*, void*), void *a); 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 Sun Apr 11 00:00:00 2021 @@ -18,18 +18,26 @@ kbmap kbin kbd latin1 uart + gpio gpio + spi spi + i2c i2c fakertc +# rtc3231 i2c + ether netif sd usb - ether netif + aoe link archbcm loopbackmedium ethermedium + sdhost usbdwc etherusb + ether4330 emmc + pitft ip tcp @@ -40,11 +48,14 @@ ipmux misc - uartmini + armv6 + trap + uartmini gpio sdmmc emmc + sdaoe sdscsi dma vcore - vfp3 coproc + vfp3 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 Sun Apr 11 00:00:00 2021 @@ -0,0 +1,72 @@ +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 + aoe + +link + archbcm2 + loopbackmedium + ethermedium + sdhost + usbdwc + etherusb + ether4330 emmc + pitft + +ip + tcp + udp + ipifc + icmp + icmp6 + ipmux + +misc + armv7 + trap + uartmini gpio + sdmmc emmc + sdaoe sdscsi + dma + vcore + vfp3 + +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 Sun Apr 11 00:00:00 2021 @@ -0,0 +1,72 @@ +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 + aoe + +link + archbcm2 + loopbackmedium + ethermedium + sdhost + usbdwc + etherusb + ether4330 emmc + pitft + +ip + tcp + udp + ipifc + icmp + icmp6 + ipmux + +misc + armv7 + trap + uartmini gpio + sdmmc emmc + sdaoe sdscsi + dma + vcore + vfp3 + +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/pi2wifi /sys/src/9/bcm/pi2wifi --- /n/sources/plan9/sys/src/9/bcm/pi2wifi Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/pi2wifi Sun Apr 11 00:00:00 2021 @@ -0,0 +1,79 @@ +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 + sdhost + usbdwc + etherusb + ether4330 emmc + pitft + +ip + tcp + udp + ipifc + icmp + icmp6 + ipmux + +misc + armv7 + trap + uartmini gpio + sdmmc emmc + dma + vcore + vfp3 + +port + int cpuserver = 0; + +boot boot #S/sdM0/ + local + tcp + +bootdir + bootwifi.rc boot + /arm/bin/rc + /rc/lib/rcmain + /arm/bin/usb/usbd + /arm/bin/auth/factotum + /arm/bin/srv + /arm/bin/aux/wpa wpa + /arm/bin/ip/ipconfig + /arm/bin/mount + /arm/bin/bind + /arm/bin/echo + /arm/bin/read + /sys/lib/firmware/brcmfmac43430-sdio.bin + /sys/lib/firmware/brcmfmac43430-sdio.txt diff -Nru /n/sources/plan9/sys/src/9/bcm/pi4 /sys/src/9/bcm/pi4 --- /n/sources/plan9/sys/src/9/bcm/pi4 Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/pi4 Sun Apr 11 00:00:00 2021 @@ -0,0 +1,76 @@ +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 + aoe + +link + archbcm4 + pci + gisb + loopbackmedium + ethermedium +# sdhost + sdhc + usbxhci +# etherusb + ethergenet ethermii + ether4330 emmc +# pitft + +ip + tcp + udp + ipifc + icmp + icmp6 + ipmux + +misc + armv7 + trap4 + uartmini gpio + sdmmc + sdaoe sdscsi + dma + vcore + vfp3 + +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/pi4cpu /sys/src/9/bcm/pi4cpu --- /n/sources/plan9/sys/src/9/bcm/pi4cpu Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/pi4cpu Sun Apr 11 00:00:00 2021 @@ -0,0 +1,76 @@ +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 + aoe + +link + archbcm4 + pci + gisb + loopbackmedium + ethermedium +# sdhost + sdhc + usbxhci +# etherusb + ethergenet ethermii + ether4330 emmc +# pitft + +ip + tcp + udp + ipifc + icmp + icmp6 + ipmux + +misc + armv7 + trap4 + uartmini gpio + sdmmc + sdaoe sdscsi + dma + vcore + vfp3 + +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/pi4wifi /sys/src/9/bcm/pi4wifi --- /n/sources/plan9/sys/src/9/bcm/pi4wifi Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/pi4wifi Sun Apr 11 00:00:00 2021 @@ -0,0 +1,86 @@ +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 + aoe + +link + archbcm4 + pci + gisb + loopbackmedium + ethermedium +# sdhost + sdhc + usbxhci +# etherusb + ethergenet ethermii + ether4330 emmc +# pitft + +ip + tcp + udp + ipifc + icmp + icmp6 + ipmux + +misc + armv7 + trap4 + uartmini gpio + sdmmc + sdaoe sdscsi + dma + vcore + vfp3 + +port + int cpuserver = 0; + +boot boot #S/sdM0/ + local + tcp + +bootdir + bootwifi.rc boot + /arm/bin/rc + /rc/lib/rcmain + /arm/bin/usb/usbd + /arm/bin/auth/factotum + /arm/bin/srv + /arm/bin/aux/wpa wpa + /arm/bin/ip/ipconfig + /arm/bin/mount + /arm/bin/bind + /arm/bin/echo + /arm/bin/read + /sys/lib/firmware/brcmfmac43455-sdio.bin + /sys/lib/firmware/brcmfmac43455-sdio.txt + /sys/lib/firmware/brcmfmac43455-sdio.clm_blob 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 Sun Apr 11 00:00:00 2021 @@ -18,18 +18,26 @@ kbmap kbin kbd latin1 uart + gpio gpio + spi spi + i2c i2c fakertc +# rtc3231 i2c + ether netif sd usb - ether netif + aoe link archbcm loopbackmedium ethermedium + sdhost usbdwc etherusb + ether4330 emmc + pitft ip tcp @@ -40,11 +48,14 @@ ipmux misc - uartmini + armv6 + trap + uartmini gpio sdmmc emmc + sdaoe sdscsi dma vcore - vfp3 coproc + vfp3 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 Sun Apr 11 00:00:00 2021 @@ -33,10 +33,12 @@ ipmux misc - uartmini + armv6 + trap + uartmini gpio sdmmc emmc dma - vfp3 coproc + vfp3 port int cpuserver = 0; 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 Sun Apr 11 00:00:00 2021 @@ -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/piwifi /sys/src/9/bcm/piwifi --- /n/sources/plan9/sys/src/9/bcm/piwifi Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/piwifi Sun Apr 11 00:00:00 2021 @@ -0,0 +1,79 @@ +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 + archbcm + loopbackmedium + ethermedium + ether4330 emmc + sdhost + usbdwc + etherusb + pitft + +ip + tcp + udp + ipifc + icmp + icmp6 + ipmux + +misc + armv6 + trap + uartmini gpio + sdmmc emmc + dma + vcore + vfp3 + +port + int cpuserver = 0; + +boot boot #S/sdM0/ + local + tcp + +bootdir + bootwifi.rc boot + /arm/bin/rc + /rc/lib/rcmain + /arm/bin/usb/usbd + /arm/bin/auth/factotum + /arm/bin/srv + /arm/bin/aux/wpa wpa + /arm/bin/ip/ipconfig + /arm/bin/mount + /arm/bin/bind + /arm/bin/echo + /arm/bin/read + /sys/lib/firmware/brcmfmac43430-sdio.bin + /sys/lib/firmware/brcmfmac43430-sdio.txt 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 Sun Apr 11 00:00:00 2021 @@ -1,93 +1,152 @@ /* - * 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. + * CPU0: + * main(PADDR(entry), PADDR(code), size); + * Copy the new kernel to its correct location in virtual memory. + * Then turn off the mmu and jump to the start of the kernel. + * + * Other CPUs: + * main(0, soc.armlocal, 0); + * Turn off the mmu, wait for a restart address from CPU0, and jump to it. */ -/* main(PADDR(entry), PADDR(code), size); */ +/* */ TEXT main(SB), 1, $-4 MOVW $setR12(SB), R12 /* copy in arguments before stack gets unmapped */ MOVW R0, R8 /* entry point */ - MOVW p2+4(FP), R9 /* source */ - MOVW n+8(FP), R10 /* byte count */ - - /* SVC mode, interrupts disabled */ - MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 - MOVW R1, CPSR + MOVW p2+4(FP), R7 /* source */ + MOVW n+8(FP), R6 /* byte count */ - /* prepare to turn off mmu */ - BL cachesoff(SB) - - /* turn off mmu */ - MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl - BIC $CpCmmu, R1 - MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + /* redo double map of first MiB PHYSDRAM = KZERO */ + MOVW 12(R(MACH)), R2 /* m->mmul1 (virtual addr) */ + MOVW $PTEDRAM, R1 /* PTE bits */ + MOVW R1, (R2) + DSB + MCR CpSC, 0, R2, C(CpCACHE), C(CpCACHEwb), CpCACHEse - /* 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 */ + /* invalidate stale TLBs */ + BARRIERS + MOVW $0, R0 + MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv + BARRIERS - /* copy the kernel to final destination */ - MOVW R8, 16(SP) /* save dest (entry point) */ - MOVW R8, R0 /* first arg is dest */ - MOVW R9, 8(SP) /* push src */ - MOVW R10, 12(SP) /* push size */ - BL memmove(SB) - MOVW 16(SP), R8 /* restore entry point */ + /* relocate PC to physical addressing */ + MOVW $_reloc(SB), R15 - /* jump to kernel physical entry point */ - B (R8) - B 0(PC) +TEXT _reloc(SB), $-4 + + /* continue with reboot only on cpu0 */ + CPUID(R2) + BEQ bootcpu -/* - * turn the caches off, double map PHYSDRAM & KZERO, invalidate TLBs, revert - * to tiny addresses. upon return, it will be safe to turn off the mmu. - * clobbers R0-R2, and returns with SP invalid. - */ -TEXT cachesoff(SB), 1, $-4 + /* other cpus wait for inter processor interrupt from cpu0 */ - /* write back and invalidate caches */ - BARRIERS + /* turn caches off, invalidate icache */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + BIC $(CpCdcache|CpCicache), R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl 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 off mmu */ MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl - BIC $(CpCdcache|CpCicache|CpCpredict), R1 + BIC $CpCmmu, R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + /* turn off SMP cache snooping */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + BIC $CpACsmp, R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + ISB + DSB + /* 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 - - /* invalidate stale TLBs before changing them */ - BARRIERS - MOVW $KZERO, R0 /* some valid virtual address */ - MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv BARRIERS - /* from here on, R0 is base of physical memory */ - MOVW $PHYSDRAM, R0 +dowfi: + WFE /* wait for event signal */ + MOVW $0xCC(R7), R1 /* inter-core .startcpu mailboxes */ + ADD R2<<4, R1 /* mailbox for this core */ + MOVW 0(R1), R8 /* content of mailbox */ + CMP $0, R8 + BEQ dowfi /* if zero, wait again */ + BL (R8) /* call received address */ + B dowfi /* shouldn't return */ - /* redo double map of first MiB PHYSDRAM = KZERO */ - MOVW $(L1+L1X(PHYSDRAM)), R2 /* address of PHYSDRAM's PTE */ - MOVW $PTEDRAM, R1 /* PTE bits */ - ORR R0, R1 /* dram base */ - MOVW R1, (R2) +bootcpu: + MOVW $PADDR(MACHADDR+MACHSIZE-4), SP - /* invalidate stale TLBs again */ - BARRIERS - MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv - BARRIERS + /* copy the kernel to final destination */ + MOVW R8, R9 /* save physical entry point */ + ADD $KZERO, R8 /* relocate dest to virtual */ + ADD $KZERO, R7 /* relocate src to virtual */ + ADD $3, R6 /* round length to words */ + BIC $3, R6 +memloop: + MOVM.IA.W (R7), [R1] + MOVM.IA.W [R1], (R8) + SUB.S $4, R6 + BNE memloop + + /* clean dcache using appropriate code for armv6 or armv7 */ + MRC CpSC, 0, R1, C(CpID), C(CpIDfeat), 7 /* Memory Model Feature Register 3 */ + TST $0xF, R1 /* hierarchical cache maintenance? */ + BNE l2wb + DSB + MOVW $0, R0 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEall + B l2wbx +l2wb: + BL cachedwb(SB) + BL l2cacheuwb(SB) +l2wbx: - /* relocate SB and return address to PHYSDRAM addressing */ - MOVW $KSEGM, R1 /* clear segment bits */ - BIC R1, R12 /* adjust SB */ - ORR R0, R12 - BIC R1, R14 /* adjust return address */ - ORR R0, R14 + /* 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 + DSB + MOVW $0, R0 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall + DSB + /* turn off mmu */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + BIC $CpCmmu, R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + BARRIERS + /* turn off SMP cache snooping */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + BIC $CpACsmp, R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + + /* invalidate dcache using appropriate code for armv6 or armv7 */ + MRC CpSC, 0, R1, C(CpID), C(CpIDfeat), 7 /* Memory Model Feature Register 3 */ + TST $0xF, R1 /* hierarchical cache maintenance */ + BNE l2inv + DSB + MOVW $0, R0 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvd), CpCACHEall + B l2invx +l2inv: + BL cachedinv(SB) + BL l2cacheuinv(SB) +l2invx: + + /* jump to restart entry point */ + MOVW R9, R8 + MOVW $0, R9 + B (R8) - RET +#define ICACHELINESZ 32 +#include "cache.v7.s" 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 Sun Apr 11 00:00:00 2021 @@ -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,16 +319,23 @@ screeninit(void) { uchar *fb; - int set; + char *p; + int set, rgbswap; ulong chan; set = screensize() == 0; fb = fbinit(set, &xgscreen.r.max.x, &xgscreen.r.max.y, &xgscreen.depth); if(fb == nil){ + xgscreen.r.max = Pt(640, 480); + xgscreen.depth = 16; + fb = fbinit(set, &xgscreen.r.max.x, &xgscreen.r.max.y, &xgscreen.depth); + } + if(fb == nil){ print("can't initialise %dx%dx%d framebuffer \n", xgscreen.r.max.x, xgscreen.r.max.y, xgscreen.depth); return; } + rgbswap = ((p = getconf("bcm2708_fb.fbswap")) != nil && *p == '1'); xgscreen.clipr = xgscreen.r; switch(xgscreen.depth){ default: @@ -332,10 +346,10 @@ chan = RGB16; break; case 24: - chan = BGR24; + chan = rgbswap? RGB24 : BGR24; break; case 32: - chan = ARGB32; + chan = rgbswap? XRGB32 : XBGR32; break; } memsetchan(&xgscreen, chan); @@ -385,6 +399,8 @@ blankscreen(int blank) { fbblank(blank); + if(lcd) + lcd->blank(blank); } static void @@ -471,9 +487,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 Sun Apr 11 00:00:00 2021 @@ -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/sdhc.c /sys/src/9/bcm/sdhc.c --- /n/sources/plan9/sys/src/9/bcm/sdhc.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/sdhc.c Sun Apr 11 00:00:00 2021 @@ -0,0 +1,579 @@ +/* + * bcm2711 sd host controller + * + * Copyright © 2012,2019 Richard Miller + * + * adapted from emmc.c - the two should really be merged + */ + +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/sd.h" + +extern SDio *sdcardlink; +#define EMMCREGS (VIRTIO+0x340000) +#define ClkEmmc2 12 + +enum { + Extfreq = 100*Mhz, /* guess external clock frequency if */ + /* not available from vcore */ + Initfreq = 400000, /* initialisation frequency for MMC */ + SDfreq = 25*Mhz, /* standard SD frequency */ + SDfreqhs = 50*Mhz, /* high speed frequency */ + DTO = 14, /* data timeout exponent (guesswork) */ + + GoIdle = 0, /* mmc/sdio go idle state */ + MMCSelect = 7, /* mmc/sd card select command */ + Setbuswidth = 6, /* mmc/sd set bus width command */ + Switchfunc = 6, /* mmc/sd switch function command */ + Voltageswitch = 11, /* md/sdio switch to 1.8V */ + IORWdirect = 52, /* sdio read/write direct command */ + IORWextended = 53, /* sdio read/write extended command */ + Appcmd = 55, /* mmc/sd application command prefix */ +}; + +enum { + /* Controller registers */ + SDMAaddr = 0x00>>2, + Blksizecnt = 0x04>>2, + Arg1 = 0x08>>2, + Cmdtm = 0x0c>>2, + Resp0 = 0x10>>2, + Resp1 = 0x14>>2, + Resp2 = 0x18>>2, + Resp3 = 0x1c>>2, + Data = 0x20>>2, + Status = 0x24>>2, + Control0 = 0x28>>2, + Control1 = 0x2c>>2, + Interrupt = 0x30>>2, + Irptmask = 0x34>>2, + Irpten = 0x38>>2, + Control2 = 0x3c>>2, + Capability = 0x40>>2, + Forceirpt = 0x50>>2, + Dmadesc = 0x58>>2, + Boottimeout = 0x70>>2, + Dbgsel = 0x74>>2, + Exrdfifocfg = 0x80>>2, + Exrdfifoen = 0x84>>2, + Tunestep = 0x88>>2, + Tunestepsstd = 0x8c>>2, + Tunestepsddr = 0x90>>2, + Spiintspt = 0xf0>>2, + Slotisrver = 0xfc>>2, + + /* Control0 */ + Busvoltage = 7<<9, + V1_8 = 5<<9, + V3_0 = 6<<9, + V3_3 = 7<<9, + Buspower = 1<<8, + Dwidth8 = 1<<5, + Dmaselect = 3<<3, + DmaSDMA = 0<<3, + DmaADMA1 = 1<<3, + DmaADMA2 = 2<<3, + Hispeed = 1<<2, + Dwidth4 = 1<<1, + Dwidth1 = 0<<1, + LED = 1<<0, + + /* Control1 */ + Srstdata = 1<<26, /* reset data circuit */ + Srstcmd = 1<<25, /* reset command circuit */ + Srsthc = 1<<24, /* reset complete host controller */ + Datatoshift = 16, /* data timeout unit exponent */ + Datatomask = 0xF0000, + Clkfreq8shift = 8, /* SD clock base divider LSBs */ + Clkfreq8mask = 0xFF00, + Clkfreqms2shift = 6, /* SD clock base divider MSBs */ + Clkfreqms2mask = 0xC0, + Clkgendiv = 0<<5, /* SD clock divided */ + Clkgenprog = 1<<5, /* SD clock programmable */ + Clken = 1<<2, /* SD clock enable */ + Clkstable = 1<<1, + Clkintlen = 1<<0, /* enable internal EMMC clocks */ + + /* Cmdtm */ + Indexshift = 24, + Suspend = 1<<22, + Resume = 2<<22, + Abort = 3<<22, + Isdata = 1<<21, + Ixchken = 1<<20, + Crcchken = 1<<19, + Respmask = 3<<16, + Respnone = 0<<16, + Resp136 = 1<<16, + Resp48 = 2<<16, + Resp48busy = 3<<16, + Multiblock = 1<<5, + Host2card = 0<<4, + Card2host = 1<<4, + Autocmd12 = 1<<2, + Autocmd23 = 2<<2, + Blkcnten = 1<<1, + Dmaen = 1<<0, + + /* Interrupt */ + Admaerr = 1<<25, + Acmderr = 1<<24, + Denderr = 1<<22, + Dcrcerr = 1<<21, + Dtoerr = 1<<20, + Cbaderr = 1<<19, + Cenderr = 1<<18, + Ccrcerr = 1<<17, + Ctoerr = 1<<16, + Err = 1<<15, + Cardintr = 1<<8, + Cardinsert = 1<<6, /* not in Broadcom datasheet */ + Readrdy = 1<<5, + Writerdy = 1<<4, + Dmaintr = 1<<3, + Datadone = 1<<1, + Cmddone = 1<<0, + + /* Status */ + Bufread = 1<<11, /* not in Broadcom datasheet */ + Bufwrite = 1<<10, /* not in Broadcom datasheet */ + Readtrans = 1<<9, + Writetrans = 1<<8, + Datactive = 1<<2, + Datinhibit = 1<<1, + Cmdinhibit = 1<<0, +}; + +static int cmdinfo[64] = { +[0] Ixchken, +[2] Resp136, +[3] Resp48 | Ixchken | Crcchken, +[5] Resp48, +[6] Resp48 | Ixchken | Crcchken, +[7] Resp48busy | Ixchken | Crcchken, +[8] Resp48 | Ixchken | Crcchken, +[9] Resp136, +[11] Resp48 | Ixchken | Crcchken, +[12] Resp48busy | Ixchken | Crcchken, +[13] Resp48 | Ixchken | Crcchken, +[16] Resp48, +[17] Resp48 | Isdata | Card2host | Ixchken | Crcchken, +[18] Resp48 | Isdata | Card2host | Multiblock | Blkcnten | Ixchken | Crcchken, +[24] Resp48 | Isdata | Host2card | Ixchken | Crcchken, +[25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken, +[41] Resp48, +[52] Resp48 | Ixchken | Crcchken, +[53] Resp48 | Ixchken | Crcchken | Isdata, +[55] Resp48 | Ixchken | Crcchken, +}; + +typedef struct Adma Adma; +typedef struct Ctlr Ctlr; + +/* + * ADMA2 descriptor + * See SD Host Controller Simplified Specification Version 2.00 + */ + +struct Adma { + u32int desc; + u32int addr; +}; + +enum { + /* desc fields */ + Valid = 1<<0, + End = 1<<1, + Int = 1<<2, + Nop = 0<<4, + Tran = 2<<4, + Link = 3<<4, + OLength = 16, + /* maximum value for Length field */ + Maxdma = ((1<<16) - 4), +}; + +struct Ctlr { + Rendez r; + Rendez cardr; + int fastclock; + ulong extclk; + int appcmd; + Adma *dma; +}; + +static Ctlr emmc; + +static void mmcinterrupt(Ureg*, void*); + +static void +WR(int reg, u32int val) +{ + u32int *r = (u32int*)EMMCREGS; + + if(0)print("WR %2.2ux %ux\n", reg<<2, val); + coherence(); + r[reg] = val; +} + +static uint +clkdiv(uint d) +{ + uint v; + + assert(d < 1<<10); + v = (d << Clkfreq8shift) & Clkfreq8mask; + v |= ((d >> 8) << Clkfreqms2shift) & Clkfreqms2mask; + return v; +} + +static Adma* +dmaalloc(void *addr, int len) +{ + int n; + uintptr a; + Adma *adma, *p; + + a = (uintptr)addr; + n = HOWMANY(len, Maxdma); + adma = sdmalloc(n * sizeof(Adma)); + for(p = adma; len > 0; p++){ + p->desc = Valid | Tran; + if(n == 1) + p->desc |= len<desc |= Maxdma<addr = dmaaddr((void*)a); + a += Maxdma; + len -= Maxdma; + n--; + } + cachedwbse(adma, (char*)p - (char*)adma); + return adma; +} + +static void +emmcclk(uint freq) +{ + u32int *r; + uint div; + int i; + + r = (u32int*)EMMCREGS; + div = emmc.extclk / (freq<<1); + if(emmc.extclk / (div<<1) > freq) + div++; + WR(Control1, clkdiv(div) | + DTO<> 16; + return snprint(inquiry, inqlen, + "BCM SD Host Controller %2.2x Version %2.2x", + ver&0xFF, ver>>8); +} + +static void +emmcenable(void) +{ + + WR(Control0, 0); + delay(1); + WR(Control0, V3_3 | Buspower | Dwidth1 | DmaADMA2); + WR(Control1, 0); + delay(1); + emmcclk(Initfreq); + WR(Irpten, 0); + WR(Irptmask, ~(Cardintr|Dmaintr)); + WR(Interrupt, ~0); + intrenable(IRQmmc, mmcinterrupt, nil, 0, "sdhc"); +} + +static int +emmccmd(u32int cmd, u32int arg, u32int *resp) +{ + u32int *r; + u32int c; + int i; + ulong now; + + r = (u32int*)EMMCREGS; + assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0); + c = (cmd << Indexshift) | cmdinfo[cmd]; + /* + * CMD6 may be Setbuswidth or Switchfunc depending on Appcmd prefix + */ + if(cmd == Switchfunc && !emmc.appcmd) + c |= Isdata|Card2host; + if(c & Isdata) + c |= Dmaen; + if(cmd == IORWextended){ + if(arg & (1<<31)) + c |= Host2card; + else + c |= Card2host; + if((r[Blksizecnt]&0xFFFF0000) != 0x10000) + c |= Multiblock | Blkcnten; + } + /* + * GoIdle indicates new card insertion: reset bus width & speed + */ + if(cmd == GoIdle){ + WR(Control0, r[Control0] & ~(Dwidth4|Hispeed)); + emmcclk(Initfreq); + } + if(r[Status] & Cmdinhibit){ + print("sdhc: need to reset Cmdinhibit intr %ux stat %ux\n", + r[Interrupt], r[Status]); + WR(Control1, r[Control1] | Srstcmd); + while(r[Control1] & Srstcmd) + ; + while(r[Status] & Cmdinhibit) + ; + } + if((r[Status] & Datinhibit) && + ((c & Isdata) || (c & Respmask) == Resp48busy)){ + print("sdhc: need to reset Datinhibit intr %ux stat %ux\n", + r[Interrupt], r[Status]); + WR(Control1, r[Control1] | Srstdata); + while(r[Control1] & Srstdata) + ; + while(r[Status] & Datinhibit) + ; + } + WR(Arg1, arg); + if((i = (r[Interrupt] & ~Cardintr)) != 0){ + if(i != Cardinsert) + print("sdhc: before command, intr was %ux\n", i); + WR(Interrupt, i); + } + WR(Cmdtm, c); + now = m->ticks; + while(((i=r[Interrupt])&(Cmddone|Err)) == 0) + if(m->ticks-now > HZ) + break; + if((i&(Cmddone|Err)) != Cmddone){ + if((i&~(Err|Cardintr)) != Ctoerr) + print("sdhc: cmd %ux arg %ux error intr %ux stat %ux\n", c, arg, i, r[Status]); + WR(Interrupt, i); + if(r[Status]&Cmdinhibit){ + WR(Control1, r[Control1]|Srstcmd); + while(r[Control1]&Srstcmd) + ; + } + error(Eio); + } + WR(Interrupt, i & ~(Datadone|Readrdy|Writerdy)); + switch(c & Respmask){ + case Resp136: + resp[0] = r[Resp0]<<8; + resp[1] = r[Resp0]>>24 | r[Resp1]<<8; + resp[2] = r[Resp1]>>24 | r[Resp2]<<8; + resp[3] = r[Resp2]>>24 | r[Resp3]<<8; + break; + case Resp48: + case Resp48busy: + resp[0] = r[Resp0]; + break; + case Respnone: + resp[0] = 0; + break; + } + if((c & Respmask) == Resp48busy){ + WR(Irpten, r[Irpten]|Datadone|Err); + tsleep(&emmc.r, datadone, 0, 3000); + i = r[Interrupt]; + if((i & Datadone) == 0) + print("sdhc: no Datadone after CMD%d\n", cmd); + if(i & Err) + print("sdhc: CMD%d error interrupt %ux\n", + cmd, r[Interrupt]); + WR(Interrupt, i); + } + /* + * Once card is selected, use faster clock + */ + if(cmd == MMCSelect){ + delay(1); + emmcclk(SDfreq); + delay(1); + emmc.fastclock = 1; + } + if(cmd == Setbuswidth){ + if(emmc.appcmd){ + /* + * If card bus width changes, change host bus width + */ + switch(arg){ + case 0: + WR(Control0, r[Control0] & ~Dwidth4); + break; + case 2: + WR(Control0, r[Control0] | Dwidth4); + break; + } + }else{ + /* + * If card switched into high speed mode, increase clock speed + */ + if((arg&0x8000000F) == 0x80000001){ + delay(1); + emmcclk(SDfreqhs); + delay(1); + } + } + }else if(cmd == IORWdirect && (arg & ~0xFF) == (1<<31|0<<28|7<<9)){ + switch(arg & 0x3){ + case 0: + WR(Control0, r[Control0] & ~Dwidth4); + break; + case 2: + WR(Control0, r[Control0] | Dwidth4); + //WR(Control0, r[Control0] | Hispeed); + break; + } + } + emmc.appcmd = (cmd == Appcmd); + return 0; +} + +static void +emmciosetup(int write, void *buf, int bsize, int bcount) +{ + int len; + + len = bsize * bcount; + assert(((uintptr)buf&3) == 0); + assert((len&3) == 0); + assert(bsize <= 2048); + WR(Blksizecnt, bcount<<16 | bsize); + if(emmc.dma) + sdfree(emmc.dma); + emmc.dma = dmaalloc(buf, len); + if(write) + cachedwbse(buf, len); + else + cachedwbinvse(buf, len); + WR(Dmadesc, dmaaddr(emmc.dma)); + okay(1); +} + +static void +emmcio(int write, uchar *buf, int len) +{ + u32int *r; + int i; + + r = (u32int*)EMMCREGS; + if(waserror()){ + okay(0); + nexterror(); + } + WR(Irpten, r[Irpten] | Datadone|Err); + tsleep(&emmc.r, datadone, 0, 3000); + WR(Irpten, r[Irpten] & ~(Datadone|Err)); + i = r[Interrupt]; + if((i & (Datadone|Err)) != Datadone){ + print("sdhc: %s error intr %ux stat %ux\n", + write? "write" : "read", i, r[Status]); + if(r[Status] & Datinhibit) + WR(Control1, r[Control1] | Srstdata); + while(r[Control1] & Srstdata) + ; + while(r[Status] & Datinhibit) + ; + WR(Interrupt, i); + error(Eio); + } + WR(Interrupt, i); + if(!write) + cachedinvse(buf, len); + poperror(); + okay(0); +} + +static void +mmcinterrupt(Ureg*, void*) +{ + u32int *r; + int i; + + r = (u32int*)EMMCREGS; + i = r[Interrupt]; + if(i&(Datadone|Err)) + wakeup(&emmc.r); + if(i&Cardintr) + wakeup(&emmc.cardr); + WR(Irpten, r[Irpten] & ~i); +} + +SDio sdiohc = { + "sdhc", + emmcinit, + emmcenable, + emmcinquiry, + emmccmd, + emmciosetup, + emmcio, +}; + +void +sdhclink(void) +{ + sdcardlink = &sdiohc; +} diff -Nru /n/sources/plan9/sys/src/9/bcm/sdhost.c /sys/src/9/bcm/sdhost.c --- /n/sources/plan9/sys/src/9/bcm/sdhost.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/sdhost.c Sun Apr 11 00:00:00 2021 @@ -0,0 +1,389 @@ +/* + * bcm2835 sdhost controller + * + * Copyright © 2016 Richard Miller + */ + +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/sd.h" + +extern SDio *sdcardlink; + +#define SDHOSTREGS (VIRTIO+0x202000) + +enum { + Extfreq = 250*Mhz, /* guess external clock frequency if vcore doesn't say */ + Initfreq = 400000, /* initialisation frequency for MMC */ + SDfreq = 25*Mhz, /* standard SD frequency */ + SDfreqhs = 50*Mhz, /* SD high speed frequency */ + FifoDepth = 4, /* "Limit fifo usage due to silicon bug" (linux driver) */ + + GoIdle = 0, /* mmc/sdio go idle state */ + MMCSelect = 7, /* mmc/sd card select command */ + Setbuswidth = 6, /* mmc/sd set bus width command */ + Switchfunc = 6, /* mmc/sd switch function command */ + Stoptransmission = 12, /* mmc/sd stop transmission command */ + Appcmd = 55, /* mmc/sd application command prefix */ +}; + +enum { + /* Controller registers */ + Cmd = 0x00>>2, + Arg = 0x04>>2, + Timeout = 0x08>>2, + Clkdiv = 0x0c>>2, + + Resp0 = 0x10>>2, + Resp1 = 0x14>>2, + Resp2 = 0x18>>2, + Resp3 = 0x1c>>2, + + Status = 0x20>>2, + Poweron = 0x30>>2, + Dbgmode = 0x34>>2, + Hconfig = 0x38>>2, + Blksize = 0x3c>>2, + Data = 0x40>>2, + Blkcount = 0x50>>2, + + /* Cmd */ + Start = 1<<15, + Failed = 1<<14, + Respmask = 7<<9, + Resp48busy = 4<<9, + Respnone = 2<<9, + Resp136 = 1<<9, + Resp48 = 0<<9, + Host2card = 1<<7, + Card2host = 1<<6, + + /* Status */ + Busyint = 1<<10, + Blkint = 1<<9, + Sdioint = 1<<8, + Rewtimeout = 1<<7, + Cmdtimeout = 1<<6, + Crcerror = 3<<4, + Fifoerror = 1<<3, + Dataflag = 1<<0, + Intstatus = (Busyint|Blkint|Sdioint|Dataflag), + Errstatus = (Rewtimeout|Cmdtimeout|Crcerror|Fifoerror), + + /* Hconfig */ + BusyintEn = 1<<10, + BlkintEn = 1<<8, + SdiointEn = 1<<5, + DataintEn = 1<<4, + Slowcard = 1<<3, + Extbus4 = 1<<2, + Intbuswide = 1<<1, + Cmdrelease = 1<<0, +}; + +static int cmdinfo[64] = { +[0] Start | Respnone, +[2] Start | Resp136, +[3] Start | Resp48, +[5] Start | Resp48, +[6] Start | Resp48, +[63] Start | Resp48 | Card2host, +[7] Start | Resp48busy, +[8] Start | Resp48, +[9] Start | Resp136, +[11] Start | Resp48, +[12] Start | Resp48busy, +[13] Start | Resp48, +[16] Start | Resp48, +[17] Start | Resp48 | Card2host, +[18] Start | Resp48 | Card2host, +[24] Start | Resp48 | Host2card, +[25] Start | Resp48 | Host2card, +[41] Start | Resp48, +[52] Start | Resp48, +[53] Start | Resp48, +[55] Start | Resp48, +}; + +typedef struct Ctlr Ctlr; + +struct Ctlr { + Rendez r; + int bcount; + int done; + ulong extclk; + int appcmd; +}; + +static Ctlr sdhost; + +static void sdhostinterrupt(Ureg*, void*); + +static void +WR(int reg, u32int val) +{ + u32int *r = (u32int*)SDHOSTREGS; + + if(0)print("WR %2.2ux %ux\n", reg<<2, val); + r[reg] = val; +} + +static int +datadone(void*) +{ + return sdhost.done; +} + +static void +sdhostclock(uint freq) +{ + uint div; + + div = sdhost.extclk / freq; + if(sdhost.extclk / freq > freq) + div++; + if(div < 2) + div = 2; + WR(Clkdiv, div - 2); +} + +static int +sdhostinit(void) +{ + u32int *r; + ulong clk; + int i; + + /* disconnect emmc and connect sdhost to SD card gpio pins */ + for(i = 48; i <= 53; i++) + gpiosel(i, Alt0); + clk = getclkrate(ClkCore); + if(clk == 0){ + clk = Extfreq; + print("sdhost: assuming external clock %lud Mhz\n", clk/1000000); + } + sdhost.extclk = clk; + sdhostclock(Initfreq); + r = (u32int*)SDHOSTREGS; + WR(Poweron, 0); + WR(Timeout, 0xF00000); + WR(Dbgmode, FINS(r[Dbgmode], 9, 10, (FifoDepth | FifoDepth<<5))); + return 0; +} + +static int +sdhostinquiry(char *inquiry, int inqlen) +{ + return snprint(inquiry, inqlen, "BCM SDHost Controller"); +} + +static void +sdhostenable(void) +{ + u32int *r; + + r = (u32int*)SDHOSTREGS; + USED(r); + WR(Poweron, 1); + delay(10); + WR(Hconfig, Intbuswide | Slowcard | BusyintEn); + WR(Clkdiv, 0x7FF); + intrenable(IRQsdhost, sdhostinterrupt, nil, 0, "sdhost"); +} + +static int +sdhostcmd(u32int cmd, u32int arg, u32int *resp) +{ + u32int *r; + u32int c; + int i; + ulong now; + + r = (u32int*)SDHOSTREGS; + assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0); + c = cmd | cmdinfo[cmd]; + /* + * CMD6 may be Setbuswidth or Switchfunc depending on Appcmd prefix + */ + if(cmd == Switchfunc && !sdhost.appcmd) + c |= Card2host; + if(cmd != Stoptransmission && (i = (r[Dbgmode] & 0xF)) > 2){ + print("sdhost: previous command stuck: Dbg=%d Cmd=%ux\n", i, r[Cmd]); + error(Eio); + } + /* + * GoIdle indicates new card insertion: reset bus width & speed + */ + if(cmd == GoIdle){ + WR(Hconfig, r[Hconfig] & ~Extbus4); + sdhostclock(Initfreq); + } + + if(r[Status] & (Errstatus|Dataflag)) + WR(Status, Errstatus|Dataflag); + sdhost.done = 0; + WR(Arg, arg); + WR(Cmd, c); + now = m->ticks; + while(((i=r[Cmd])&(Start|Failed)) == Start) + if(m->ticks-now > HZ) + break; + if((i&(Start|Failed)) != 0){ + if(r[Status] != Cmdtimeout) + print("sdhost: cmd %ux arg %ux error stat %ux\n", i, arg, r[Status]); + i = r[Status]; + WR(Status, i); + error(Eio); + } + switch(c & Respmask){ + case Resp136: + resp[0] = r[Resp0]; + resp[1] = r[Resp1]; + resp[2] = r[Resp2]; + resp[3] = r[Resp3]; + break; + case Resp48: + case Resp48busy: + resp[0] = r[Resp0]; + break; + case Respnone: + resp[0] = 0; + break; + } + if((c & Respmask) == Resp48busy){ + tsleep(&sdhost.r, datadone, 0, 3000); + } + switch(cmd) { + case MMCSelect: + /* + * Once card is selected, use faster clock + */ + delay(1); + sdhostclock(SDfreq); + delay(1); + break; + case Setbuswidth: + if(sdhost.appcmd){ + /* + * If card bus width changes, change host bus width + */ + switch(arg){ + case 0: + WR(Hconfig, r[Hconfig] & ~Extbus4); + break; + case 2: + WR(Hconfig, r[Hconfig] | Extbus4); + break; + } + }else{ + /* + * If card switched into high speed mode, increase clock speed + */ + if((arg&0x8000000F) == 0x80000001){ + delay(1); + sdhostclock(SDfreqhs); + delay(1); + } + } + break; + } + sdhost.appcmd = (cmd == Appcmd); + return 0; +} + +void +sdhostiosetup(int write, void *buf, int bsize, int bcount) +{ + USED(write); + USED(buf); + + sdhost.bcount = bcount; + WR(Blksize, bsize); + WR(Blkcount, bcount); +} + +static void +sdhostio(int write, uchar *buf, int len) +{ + u32int *r; + int piolen; + u32int w; + + r = (u32int*)SDHOSTREGS; + assert((len&3) == 0); + assert((PTR2UINT(buf)&3) == 0); + okay(1); + if(waserror()){ + okay(0); + nexterror(); + } + /* + * According to comments in the linux driver, the hardware "doesn't + * manage the FIFO DREQs properly for multi-block transfers" on input, + * so the dma must be stopped early and the last 3 words fetched with pio + */ + piolen = 0; + if(!write && sdhost.bcount > 1){ + piolen = (FifoDepth-1) * sizeof(u32int); + len -= piolen; + } + if(write) + dmastart(DmaChanSdhost, DmaDevSdhost, DmaM2D, + buf, &r[Data], len); + else + dmastart(DmaChanSdhost, DmaDevSdhost, DmaD2M, + &r[Data], buf, len); + if(dmawait(DmaChanSdhost) < 0) + error(Eio); + if(!write){ + cachedinvse(buf, len); + buf += len; + for(; piolen > 0; piolen -= sizeof(u32int)){ + if((r[Dbgmode] & 0x1F00) == 0){ + print("sdhost: FIFO empty after short dma read\n"); + error(Eio); + } + w = r[Data]; + *((u32int*)buf) = w; + buf += sizeof(u32int); + } + } + poperror(); + okay(0); +} + +static void +sdhostinterrupt(Ureg*, void*) +{ + u32int *r; + int i; + + r = (u32int*)SDHOSTREGS; + i = r[Status]; + WR(Status, i); + if(i & Busyint){ + sdhost.done = 1; + wakeup(&sdhost.r); + } +} + +SDio sdiohost = { + "sdhost", + sdhostinit, + sdhostenable, + sdhostinquiry, + sdhostcmd, + sdhostiosetup, + sdhostio, +}; + +void +sdhostlink(void) +{ + sdcardlink = &sdiohost; +} diff -Nru /n/sources/plan9/sys/src/9/bcm/sdmmc.c /sys/src/9/bcm/sdmmc.c --- /n/sources/plan9/sys/src/9/bcm/sdmmc.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/sdmmc.c Sun Apr 11 00:00:00 2021 @@ -0,0 +1,355 @@ +/* + * mmc / sd memory card + * + * Copyright © 2012 Richard Miller + * + * Assumes only one card on the bus + */ + +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "../port/sd.h" + +#define CSD(end, start) rbits(csd, start, (end)-(start)+1) + +typedef struct Ctlr Ctlr; + +enum { + Inittimeout = 15, + Multiblock = 1, + + /* Commands */ + GO_IDLE_STATE = 0, + ALL_SEND_CID = 2, + SEND_RELATIVE_ADDR= 3, + SWITCH_FUNC = 6, + SELECT_CARD = 7, + SD_SEND_IF_COND = 8, + SEND_CSD = 9, + STOP_TRANSMISSION= 12, + SEND_STATUS = 13, + SET_BLOCKLEN = 16, + READ_SINGLE_BLOCK= 17, + READ_MULTIPLE_BLOCK= 18, + WRITE_BLOCK = 24, + WRITE_MULTIPLE_BLOCK= 25, + APP_CMD = 55, /* prefix for following app-specific commands */ + SET_BUS_WIDTH = 6, + SD_SEND_OP_COND = 41, + + /* Command arguments */ + /* SD_SEND_IF_COND */ + Voltage = 1<<8, + Checkpattern = 0x42, + + /* SELECT_CARD */ + Rcashift = 16, + + /* SD_SEND_OP_COND */ + Hcs = 1<<30, /* host supports SDHC & SDXC */ + Ccs = 1<<30, /* card is SDHC or SDXC */ + V3_3 = 3<<20, /* 3.2-3.4 volts */ + + /* SET_BUS_WIDTH */ + Width1 = 0<<0, + Width4 = 2<<0, + + /* SWITCH_FUNC */ + Dfltspeed = 0<<0, + Hispeed = 1<<0, + Checkfunc = 0x00FFFFF0, + Setfunc = 0x80FFFFF0, + Funcbytes = 64, + + /* OCR (operating conditions register) */ + Powerup = 1<<31, +}; + +struct Ctlr { + SDev *dev; + SDio *io; + /* SD card registers */ + u16int rca; + u32int ocr; + u32int cid[4]; + u32int csd[4]; +}; + +SDio *sdcardlink; + +extern SDifc sdmmcifc; + +static uint +rbits(u32int *p, uint start, uint len) +{ + uint w, off, v; + + w = start / 32; + off = start % 32; + if(off == 0) + v = p[w]; + else + v = p[w] >> off | p[w+1] << (32-off); + if(len < 32) + return v & ((1<secsize = 1 << CSD(83, 80); + switch(CSD(127, 126)){ + case 0: /* CSD version 1 */ + csize = CSD(73, 62); + mult = CSD(49, 47); + unit->sectors = (csize+1) * (1<<(mult+2)); + break; + case 1: /* CSD version 2 */ + csize = CSD(69, 48); + unit->sectors = (csize+1) * 512LL*KiB / unit->secsize; + break; + } + if(unit->secsize == 1024){ + unit->sectors <<= 1; + unit->secsize = 512; + } +} + +static SDev* +mmcpnp(void) +{ + SDev *sdev; + Ctlr *ctl; + + if(sdcardlink == nil) + sdcardlink = &sdio; + if(sdcardlink->init() < 0) + return nil; + sdev = malloc(sizeof(SDev)); + if(sdev == nil) + return nil; + ctl = malloc(sizeof(Ctlr)); + if(ctl == nil){ + free(sdev); + return nil; + } + sdev->idno = 'M'; + sdev->ifc = &sdmmcifc; + sdev->nunit = 1; + sdev->ctlr = ctl; + ctl->dev = sdev; + ctl->io = sdcardlink; + return sdev; +} + +static int +mmcverify(SDunit *unit) +{ + int n; + Ctlr *ctl; + + ctl = unit->dev->ctlr; + n = ctl->io->inquiry((char*)&unit->inquiry[8], sizeof(unit->inquiry)-8); + if(n < 0) + return 0; + unit->inquiry[0] = SDperdisk; + unit->inquiry[1] = SDinq1removable; + unit->inquiry[4] = sizeof(unit->inquiry)-4; + return 1; +} + +static int +mmcenable(SDev* dev) +{ + Ctlr *ctl; + + ctl = dev->ctlr; + ctl->io->enable(); + return 1; +} + +static void +mmcswitchfunc(SDio *io, int arg) +{ + uchar *buf; + int n; + u32int r[4]; + + n = Funcbytes; + buf = sdmalloc(n); + if(waserror()){ + print("mmcswitchfunc error\n"); + sdfree(buf); + nexterror(); + } + io->iosetup(0, buf, n, 1); + io->cmd(SWITCH_FUNC, arg, r); + io->io(0, buf, n); + sdfree(buf); + poperror(); +} + +static int +mmconline(SDunit *unit) +{ + int hcs, i; + u32int r[4]; + Ctlr *ctl; + SDio *io; + + ctl = unit->dev->ctlr; + io = ctl->io; + assert(unit->subno == 0); + + if(waserror()){ + unit->sectors = 0; + return 0; + } + if(unit->sectors != 0){ + io->cmd(SEND_STATUS, ctl->rca<sectors = 0; + poperror(); + return 2; + } + io->cmd(GO_IDLE_STATE, 0, r); + hcs = 0; + if(!waserror()){ + io->cmd(SD_SEND_IF_COND, Voltage|Checkpattern, r); + if(r[0] == (Voltage|Checkpattern)) /* SD 2.0 or above */ + hcs = Hcs; + poperror(); + } + for(i = 0; i < Inittimeout; i++){ + delay(100); + io->cmd(APP_CMD, 0, r); + io->cmd(SD_SEND_OP_COND, hcs|V3_3, r); + if(r[0] & Powerup) + break; + } + if(i == Inittimeout){ + print("sdmmc: card won't power up\n"); + error(Eio); + } + poperror(); + ctl->ocr = r[0]; + io->cmd(ALL_SEND_CID, 0, r); + memmove(ctl->cid, r, sizeof ctl->cid); + io->cmd(SEND_RELATIVE_ADDR, 0, r); + ctl->rca = r[0]>>16; + io->cmd(SEND_CSD, ctl->rca<csd, r, sizeof ctl->csd); + identify(unit, ctl->csd); + io->cmd(SELECT_CARD, ctl->rca<cmd(SET_BLOCKLEN, unit->secsize, r); + io->cmd(APP_CMD, ctl->rca<cmd(SET_BUS_WIDTH, Width4, r); + if(!waserror()){ + mmcswitchfunc(io, Hispeed|Setfunc); + poperror(); + } + poperror(); + return 1; +} + +static int +mmcrctl(SDunit *unit, char *p, int l) +{ + Ctlr *ctl; + int i, n; + + ctl = unit->dev->ctlr; + assert(unit->subno == 0); + if(unit->sectors == 0){ + mmconline(unit); + if(unit->sectors == 0) + return 0; + } + n = snprint(p, l, "rca %4.4ux ocr %8.8ux\ncid ", ctl->rca, ctl->ocr); + for(i = nelem(ctl->cid)-1; i >= 0; i--) + n += snprint(p+n, l-n, "%8.8ux", ctl->cid[i]); + n += snprint(p+n, l-n, " csd "); + for(i = nelem(ctl->csd)-1; i >= 0; i--) + n += snprint(p+n, l-n, "%8.8ux", ctl->csd[i]); + n += snprint(p+n, l-n, "\ngeometry %llud %ld %lld 255 63\n", + unit->sectors, unit->secsize, unit->sectors / (255*63)); + return n; +} + +static long +mmcbio(SDunit *unit, int lun, int write, void *data, long nb, uvlong bno) +{ + int len, tries; + ulong b; + u32int r[4]; + uchar *buf; + Ctlr *ctl; + SDio *io; + + USED(lun); + ctl = unit->dev->ctlr; + io = ctl->io; + assert(unit->subno == 0); + if(unit->sectors == 0) + error("media change"); + buf = data; + len = unit->secsize; + if(Multiblock){ + b = bno; + tries = 0; + while(waserror()) + if(++tries == 3) + nexterror(); + io->iosetup(write, buf, len, nb); + if(waserror()){ + io->cmd(STOP_TRANSMISSION, 0, r); + nexterror(); + } + io->cmd(write? WRITE_MULTIPLE_BLOCK: READ_MULTIPLE_BLOCK, + ctl->ocr & Ccs? b: b * len, r); + io->io(write, buf, nb * len); + poperror(); + io->cmd(STOP_TRANSMISSION, 0, r); + poperror(); + b += nb; + }else{ + for(b = bno; b < bno + nb; b++){ + io->iosetup(write, buf, len, 1); + io->cmd(write? WRITE_BLOCK : READ_SINGLE_BLOCK, + ctl->ocr & Ccs? b: b * len, r); + io->io(write, buf, len); + buf += len; + } + } + return (b - bno) * len; +} + +static int +mmcrio(SDreq*) +{ + return -1; +} + +SDifc sdmmcifc = { + .name = "mmc", + .pnp = mmcpnp, + .enable = mmcenable, + .verify = mmcverify, + .online = mmconline, + .rctl = mmcrctl, + .bio = mmcbio, + .rio = mmcrio, +}; 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 Sun Apr 11 00:00:00 2021 @@ -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/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 Sun Apr 11 00:00:00 2021 @@ -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); @@ -320,10 +401,9 @@ fsr = (x>>7) & 0x8 | x & 0x7; switch(fsr){ case 0x02: /* instruction debug event (BKPT) */ - if(user){ - snprint(buf, sizeof buf, "sys: breakpoint"); - postnote(up, 1, buf, NDebug); - }else{ + if(user) + postnote(up, 1, "sys: breakpoint", NDebug); + else{ iprint("kernel bkpt: pc %#lux inst %#ux\n", ureg->pc, *(u32int*)ureg->pc); panic("kernel bkpt"); @@ -352,8 +432,7 @@ case 0x3: /* access flag fault (section) */ if(user){ snprint(buf, sizeof buf, - "sys: alignment: pc %#lux va %#p\n", - ureg->pc, va); + "sys: alignment: va %#p", va); postnote(up, 1, buf, NDebug); } else panic("kernel alignment: pc %#lux va %#p", ureg->pc, va); @@ -389,8 +468,7 @@ /* domain fault, accessing something we shouldn't */ if(user){ snprint(buf, sizeof buf, - "sys: access violation: pc %#lux va %#p\n", - ureg->pc, va); + "sys: access violation: va %#p", va); postnote(up, 1, buf, NDebug); } else panic("kernel access violation: pc %#lux va %#p", @@ -411,12 +489,8 @@ else{ /* look for floating point instructions to interpret */ rv = fpuemu(ureg); - if(rv == 0){ - snprint(buf, sizeof buf, - "undefined instruction: pc %#lux\n", - ureg->pc); - postnote(up, 1, buf, NDebug); - } + if(rv == 0) + postnote(up, 1, "sys: undefined instruction", NDebug); } }else{ if (ureg->pc & 3) { @@ -524,13 +598,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/trap4.c /sys/src/9/bcm/trap4.c --- /n/sources/plan9/sys/src/9/bcm/trap4.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/trap4.c Sun Apr 11 00:00:00 2021 @@ -0,0 +1,993 @@ +/* + * Adapted from ../teg2/trap.c + * + * arm mpcore generic interrupt controller (gic) v1 + * traps, exceptions, interrupts, system calls. + * + * there are two pieces: the interrupt distributor and the cpu interface. + * + * memset or memmove on any of the distributor registers generates an + * exception like this one: + * panic: external abort 0x28 pc 0xc048bf68 addr 0x50041800 + * + * we use l1 and l2 cache ops to force vectors to be visible everywhere. + * + * apparently irqs 0—15 (SGIs) are always enabled. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "ureg.h" +#include "arm.h" + +#define IRQLOCAL(irq) ((irq) - IRQlocal + 13 + 16) +#define IRQGLOBAL(irq) ((irq) + 64 + 32) + +#define ISSGI(irq) ((uint)(irq) < Nppi) + +enum { + Debug = 0, + + Intrdist = 0x41000, + Intrcpu = 0x42000, + + Nvec = 8, /* # of vectors at start of lexception.s */ + Bi2long = BI2BY * sizeof(long), + Nirqs = 1024, + Nsgi = 16, /* software-generated (inter-processor) intrs */ + Nppi = 32, /* sgis + other private peripheral intrs */ +}; + +typedef struct Intrcpuregs Intrcpuregs; +typedef struct Intrdistregs Intrdistregs; + +/* + * almost this entire register set is buggered. + * the distributor is supposed to be per-system, not per-cpu, + * yet some registers are banked per-cpu, as marked. + */ +struct Intrdistregs { /* distributor */ + ulong ctl; + ulong ctlrtype; + ulong distid; + uchar _pad0[0x80 - 0xc]; + + /* botch: *[0] are banked per-cpu from here */ + /* bit maps */ + ulong grp[32]; /* in group 1 (non-secure) */ + ulong setena[32]; /* forward to cpu interfaces */ + ulong clrena[32]; + ulong setpend[32]; + ulong clrpend[32]; + ulong setact[32]; /* active? */ + ulong clract[32]; + /* botch: *[0] are banked per-cpu until here */ + + uchar pri[1020]; /* botch: pri[0] — pri[7] are banked per-cpu */ + ulong _rsrvd1; + /* botch: targ[0] through targ[7] are banked per-cpu and RO */ + uchar targ[1020]; /* byte bit maps: cpu targets indexed by intr */ + ulong _rsrvd2; + /* botch: cfg[1] is banked per-cpu */ + ulong cfg[64]; /* bit pairs: edge? 1-N? */ + ulong _pad1[64]; + ulong nsac[64]; /* bit pairs (v2 only) */ + + /* software-generated intrs (a.k.a. sgi) */ + ulong swgen; /* intr targets */ + uchar _pad2[0xf10 - 0xf04]; + uchar clrsgipend[16]; /* bit map (v2 only) */ + uchar setsgipend[16]; /* bit map (v2 only) */ +}; + +enum { + /* ctl bits */ + Forw2cpuif = 1, + + /* ctlrtype bits */ + Cpunoshft = 5, + Cpunomask = MASK(3), + Intrlines = MASK(5), + + /* cfg bits */ + Level = 0<<1, + Edge = 1<<1, /* edge-, not level-sensitive */ + Toall = 0<<0, + To1 = 1<<0, /* vs. to all */ + + /* swgen bits */ + Totargets = 0, + Tonotme = 1<<24, + Tome = 2<<24, +}; + +/* each cpu sees its own registers at the same base address ((ARMLOCAL+Intrcpu)) */ +struct Intrcpuregs { + ulong ctl; + ulong primask; + + ulong binpt; /* group pri vs subpri split */ + ulong ack; + ulong end; + ulong runpri; + ulong hipripend; + + /* aliased regs (secure, for group 1) */ + ulong alibinpt; + ulong aliack; /* (v2 only) */ + ulong aliend; /* (v2 only) */ + ulong alihipripend; /* (v2 only) */ + + uchar _pad0[0xd0 - 0x2c]; + ulong actpri[4]; /* (v2 only) */ + ulong nsactpri[4]; /* (v2 only) */ + + uchar _pad0[0xfc - 0xf0]; + ulong ifid; /* ro */ + + uchar _pad0[0x1000 - 0x100]; + ulong deact; /* wo (v2 only) */ +}; + +enum { + /* ctl bits */ + Enable = 1, + Eoinodeact = 1<<9, /* (v2 only) */ + + /* (ali) ack/end/hipriend/deact bits */ + Intrmask = MASK(10), + Cpuidshift = 10, + Cpuidmask = MASK(3), + + /* ifid bits */ + Archversshift = 16, + Archversmask = MASK(4), +}; + +typedef struct Vctl Vctl; +typedef struct Vctl { + Vctl* next; /* handlers on this vector */ + char *name; /* of driver, xallocated */ + void (*f)(Ureg*, void*); /* handler to call */ + void* a; /* argument to call it with */ +} Vctl; + +static Lock vctllock; +static Vctl* vctl[Nirqs]; + +/* + * Layout at virtual address 0. + */ +typedef struct Vpage0 { + void (*vectors[Nvec])(void); + u32int vtable[Nvec]; +} Vpage0; + +enum +{ + Ntimevec = 20 /* number of time buckets for each intr */ +}; +ulong intrtimes[Nirqs][Ntimevec]; + +int irqtooearly = 0; + +static ulong shadena[32]; /* copy of enable bits, saved by intcmaskall */ +static Lock distlock; + +extern int notify(Ureg*); + +static void dumpstackwithureg(Ureg *ureg); + +void +printrs(int base, ulong word) +{ + int bit; + + for (bit = 0; word; bit++, word >>= 1) + if (word & 1) + iprint(" %d", base + bit); +} + +void +dumpintrs(char *what, ulong *bits) +{ + int i, first, some; + ulong word; + Intrdistregs *idp = (Intrdistregs *)(ARMLOCAL+Intrdist); + + first = 1; + some = 0; + USED(idp); + for (i = 0; i < nelem(idp->setpend); i++) { + word = bits[i]; + if (word) { + if (first) { + first = 0; + iprint("%s", what); + } + some = 1; + printrs(i * Bi2long, word); + } + } + if (!some) + iprint("%s none", what); + iprint("\n"); +} + +void +dumpintrpend(void) +{ + Intrdistregs *idp = (Intrdistregs *)(ARMLOCAL+Intrdist); + + iprint("\ncpu%d gic regs:\n", m->machno); + dumpintrs("group 1", idp->grp); + dumpintrs("enabled", idp->setena); + dumpintrs("pending", idp->setpend); + dumpintrs("active ", idp->setact); +} + +/* + * keep histogram of interrupt service times + */ +void +intrtime(Mach*, int vno) +{ + ulong diff; + ulong x; + + x = perfticks(); + diff = x - m->perf.intrts; + m->perf.intrts = x; + + m->perf.inintr += diff; + if(up == nil && m->perf.inidle > diff) + m->perf.inidle -= diff; + + if (m->cpumhz == 0) + return; /* don't divide by zero */ + diff /= m->cpumhz*100; /* quantum = 100µsec */ + if(diff >= Ntimevec) + diff = Ntimevec-1; + if ((uint)vno >= Nirqs) + vno = Nirqs-1; + intrtimes[vno][diff]++; +} + +static ulong +intack(Intrcpuregs *icp) +{ + return icp->ack & Intrmask; +} + +static void +intdismiss(Intrcpuregs *icp, ulong ack) +{ + icp->end = ack; + coherence(); +} + +static int +irqinuse(uint irq) +{ + Intrdistregs *idp = (Intrdistregs *)(ARMLOCAL+Intrdist); + + return idp->setena[irq / Bi2long] & (1 << (irq % Bi2long)); +} + +void +intcunmask(uint irq) +{ + Intrdistregs *idp = (Intrdistregs *)(ARMLOCAL+Intrdist); + + ilock(&distlock); + idp->setena[irq / Bi2long] = 1 << (irq % Bi2long); + iunlock(&distlock); +} + +void +intcmask(uint irq) +{ + Intrdistregs *idp = (Intrdistregs *)(ARMLOCAL+Intrdist); + + ilock(&distlock); + idp->clrena[irq / Bi2long] = 1 << (irq % Bi2long); + iunlock(&distlock); +} + +static void +intcmaskall(Intrdistregs *idp) /* mask all intrs for all cpus */ +{ + int i; + + for (i = 0; i < nelem(idp->setena); i++) + shadena[i] = idp->setena[i]; + for (i = 0; i < nelem(idp->clrena); i++) + idp->clrena[i] = ~0; + coherence(); +} + +static void +intcunmaskall(Intrdistregs *idp) /* unused */ +{ + int i; + + for (i = 0; i < nelem(idp->setena); i++) + idp->setena[i] = shadena[i]; + coherence(); +} + +static ulong +permintrs(Intrdistregs *idp, int base, int r) +{ + ulong perms; + + idp->clrena[r] = ~0; /* disable all */ + coherence(); + perms = idp->clrena[r]; + if (perms) { + iprint("perm intrs:"); + printrs(base, perms); + iprint("\n"); + } + return perms; +} + +static void +intrcfg(Intrdistregs *idp) +{ + int i, cpumask; + ulong pat; + + /* set up all interrupts as level-sensitive, to one cpu (0) */ + pat = 0; + for (i = 0; i < Bi2long; i += 2) + pat |= (Level | To1) << i; + + if (m->machno == 0) { /* system-wide & cpu0 cfg */ + for (i = 0; i < nelem(idp->grp); i++) + idp->grp[i] = 0; /* secure */ + for (i = 0; i < nelem(idp->pri); i++) + idp->pri[i] = 0; /* highest priority */ + /* set up all interrupts as level-sensitive, to one cpu (0) */ + for (i = 0; i < nelem(idp->cfg); i++) + idp->cfg[i] = pat; + /* first Nppi are read-only for SGIs and PPIs */ + cpumask = 1<<0; /* just cpu 0 */ + for (i = Nppi; i < sizeof idp->targ; i++) + idp->targ[i] = cpumask; + coherence(); + + intcmaskall(idp); + for (i = 0; i < nelem(idp->clrena); i++) { + // permintrs(idp, i * Bi2long, i); + idp->clrpend[i] = idp->clract[i] = idp->clrena[i] = ~0; + } + } else { /* per-cpu config */ + idp->grp[0] = 0; /* secure */ + for (i = 0; i < 8; i++) + idp->pri[i] = 0; /* highest priority */ + /* idp->targ[0 through Nppi-1] are supposed to be read-only */ + for (i = 0; i < Nppi; i++) + idp->targ[i] = 1<machno; + idp->cfg[1] = pat; + coherence(); + + // permintrs(idp, i * Bi2long, i); + idp->clrpend[0] = idp->clract[0] = idp->clrena[0] = ~0; + /* on cpu1, irq Extpmuirq (118) is always pending here */ + } + coherence(); +} + +void +intrto(int cpu, int irq) +{ + Intrdistregs *idp = (Intrdistregs *)(ARMLOCAL+Intrdist); + + /* first Nppi are read-only for SGIs and the like */ + ilock(&distlock); + idp->targ[irq] = 1 << cpu; + iunlock(&distlock); +} + +void +intrsto(int cpu) /* unused */ +{ + int i; + Intrdistregs *idp = (Intrdistregs *)(ARMLOCAL+Intrdist); + + /* first Nppi are read-only for SGIs and the like */ + for (i = Nppi; i < sizeof idp->targ; i++) + intrto(cpu, i); + USED(idp); +} + +void +intrcpu(int cpu) +{ + Intrdistregs *idp = (Intrdistregs *)(ARMLOCAL+Intrdist); + + ilock(&distlock); + idp->swgen = Totargets | 1 << (cpu + 16) | m->machno; + iunlock(&distlock); +} + +/* + * set up for exceptions + */ +void +trapinit(void) +{ + int s; + Intrdistregs *idp = (Intrdistregs *)(ARMLOCAL+Intrdist); + Intrcpuregs *icp = (Intrcpuregs *)(ARMLOCAL+Intrcpu); + Vpage0 *vpage0; + enum { Vecsize = sizeof vpage0->vectors + sizeof vpage0->vtable, }; + + /* + * set up the exception vectors, high and low. + * + * we can't use cache ops on HVECTORS address, since they + * work on virtual addresses, and only those that have a + * physical address == PADDR(virtual). + */ + if (m->machno == 0) { + vpage0 = (Vpage0*)HVECTORS; + memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors)); + memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable)); + + vpage0 = (Vpage0*)KADDR(0); + memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors)); + memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable)); + + cacheuwbinv(); + l2cacheuwbinv(); + } + + /* + * set up the stack pointers for the exception modes for this cpu. + * they point to small `save areas' in Mach, not actual stacks. + */ + s = splhi(); /* make these modes ignore intrs too */ + setr13(PsrMfiq, (u32int*)(FIQSTKTOP)); + setr13(PsrMirq, m->sirq); + setr13(PsrMabt, m->sabt); + setr13(PsrMund, m->sund); + setr13(PsrMsys, m->ssys); + splx(s); + + assert((idp->distid & MASK(12)) == 0x43b); /* made by arm */ + assert((icp->ifid & MASK(12)) == 0x43b); /* made by arm */ + + ilock(&distlock); + idp->ctl = 0; + icp->ctl = 0; + coherence(); + + intrcfg(idp); /* some per-cpu cfg here */ + + icp->ctl = Enable; + icp->primask = (uchar)~0; /* let all priorities through */ + coherence(); + + idp->ctl = Forw2cpuif; + iunlock(&distlock); +} + +void +intrsoff(void) +{ + ilock(&distlock); + intcmaskall((Intrdistregs *)(ARMLOCAL+Intrdist)); + iunlock(&distlock); +} + +void +intrcpushutdown(void) +{ + Intrcpuregs *icp = (Intrcpuregs *)(ARMLOCAL+Intrcpu); + + icp->ctl = 0; + icp->primask = 0; /* let no priorities through */ + coherence(); +} + +/* called from cpu0 after other cpus are shutdown */ +void +intrshutdown(void) +{ + Intrdistregs *idp = (Intrdistregs *)(ARMLOCAL+Intrdist); + + intrsoff(); + idp->ctl = 0; + intrcpushutdown(); +} + +/* + * enable an irq interrupt + * note that the same private interrupt may be enabled on multiple cpus + */ +void +irqenable(int irq, void (*f)(Ureg*, void*), void* a) +{ + Vctl *v; + int ena; + static char name[] = "anon"; + + /* permute irq numbers for pi4 */ + if(irq >= IRQlocal) + irq = IRQLOCAL(irq); + else + irq = IRQGLOBAL(irq); + if(irq >= nelem(vctl)) + panic("irqenable irq %d", irq); + + if (irqtooearly) { + iprint("irqenable for %d %s called too early\n", irq, name); + return; + } + + /* + * if in use, could be a private interrupt on a secondary cpu, + * or a shared irq number (eg emmc and sdhc) + */ + ena = 1; + if(!ISSGI(irq) || vctl[irq] == nil) { + v = malloc(sizeof(Vctl)); + if (v == nil) + panic("irqenable: malloc Vctl"); + v->f = f; + v->a = a; + v->name = malloc(strlen(name)+1); + if (v->name == nil) + panic("irqenable: malloc name"); + strcpy(v->name, name); + + lock(&vctllock); + v->next = vctl[irq]; + if (v->next == nil) + vctl[irq] = v; + else if (!ISSGI(irq)) { + /* shared irq number */ + vctl[irq] = v; + ena = 0; + } else { + /* allocation race: someone else did it first */ + free(v->name); + free(v); + } + unlock(&vctllock); + } + if (ena) { + intdismiss((Intrcpuregs *)(ARMLOCAL+Intrcpu), irq); + intcunmask(irq); + } +} + +/* + * called by trap to handle access faults + */ +static void +faultarm(Ureg *ureg, uintptr va, int user, int read) +{ + int n, insyscall; + + if(up == nil) { + dumpstackwithureg(ureg); + panic("faultarm: cpu%d: nil up, %sing %#p at %#p", + m->machno, (read? "read": "writ"), va, ureg->pc); + } + insyscall = up->insyscall; + up->insyscall = 1; + + n = fault(va, read); /* goes spllo */ + splhi(); + if(n < 0){ + char buf[ERRMAX]; + + if(!user){ + dumpstackwithureg(ureg); + panic("fault: cpu%d: kernel %sing %#p at %#p", + m->machno, read? "read": "writ", va, ureg->pc); + } + /* don't dump registers; programs suicide all the time */ + snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p", + read? "read": "write", va); + postnote(up, 1, buf, NDebug); + } + up->insyscall = insyscall; +} + +/* + * called by trap to handle interrupts. + * returns true iff a clock interrupt, thus maybe reschedule. + */ +static int +irq(Ureg* ureg) +{ + int clockintr, ack; + uint irqno, handled; + Intrcpuregs *icp = (Intrcpuregs *)(ARMLOCAL+Intrcpu); + Vctl *v; + + clockintr = 0; +again: + ack = intack(icp); + irqno = ack & Intrmask; + + if(irqno == 1023) + return clockintr; + if(irqno == IRQGLOBAL(IRQclock) || irqno == IRQLOCAL(IRQcntpns)) + clockintr = 1; + + handled = 0; + for(v = vctl[irqno]; v != nil; v = v->next) + if (v->f) { + if (islo()) + panic("trap: pl0 before trap handler for %s", + v->name); + coherence(); + v->f(ureg, v->a); + coherence(); + if (islo()) + panic("trap: %s lowered pl", v->name); +// splhi(); /* in case v->f lowered pl */ + handled++; + } + if(!handled){ + if (irqno >= 1022){ + iprint("cpu%d: ignoring spurious interrupt\n", m->machno); + return clockintr; + }else { + intcmask(irqno); + iprint("cpu%d: unexpected interrupt %d, now masked\n", + m->machno, irqno); + } + } + + intdismiss(icp, ack); + intrtime(m, irqno); + goto again; +} + +/* + * returns 1 if the instruction writes memory, 0 otherwise + */ +int +writetomem(ulong inst) +{ + /* swap always write memory */ + if((inst & 0x0FC00000) == 0x01000000) + return 1; + + /* loads and stores are distinguished by bit 20 */ + if(inst & (1<<20)) + return 0; + + return 1; +} + +static void +datafault(Ureg *ureg, int user) +{ + int x; + ulong inst, fsr; + uintptr va; + + va = farget(); + + inst = *(ulong*)(ureg->pc); + /* bits 12 and 10 have to be concatenated with status */ + x = fsrget(); + fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf; + switch(fsr){ + default: + case 0xa: /* ? was under external abort */ + panic("unknown data fault, 6b fsr %#lux", fsr); + break; + case 0x0: + panic("vector exception at %#lux", ureg->pc); + break; + case 0x1: /* alignment fault */ + case 0x3: /* access flag fault (section) */ + if(user){ + char buf[ERRMAX]; + + snprint(buf, sizeof buf, + "sys: alignment: pc %#lux va %#p\n", + ureg->pc, va); + postnote(up, 1, buf, NDebug); + } else { + dumpstackwithureg(ureg); + panic("kernel alignment: pc %#lux va %#p", ureg->pc, va); + } + break; + case 0x2: + panic("terminal exception at %#lux", ureg->pc); + break; + case 0x4: /* icache maint fault */ + case 0x6: /* access flag fault (page) */ + case 0x8: /* precise external abort, non-xlat'n */ + case 0x28: + case 0x16: /* imprecise ext. abort, non-xlt'n */ + case 0x36: + panic("external non-translation abort %#lux pc %#lux addr %#p", + fsr, ureg->pc, va); + break; + case 0xc: /* l1 translation, precise ext. abort */ + case 0x2c: + case 0xe: /* l2 translation, precise ext. abort */ + case 0x2e: + panic("external translation abort %#lux pc %#lux addr %#p", + fsr, ureg->pc, va); + break; + case 0x1c: /* l1 translation, precise parity err */ + case 0x1e: /* l2 translation, precise parity err */ + case 0x18: /* imprecise parity or ecc err */ + panic("translation parity error %#lux pc %#lux addr %#p", + fsr, ureg->pc, va); + break; + case 0x5: /* translation fault, no section entry */ + case 0x7: /* translation fault, no page entry */ + faultarm(ureg, va, user, !writetomem(inst)); + break; + case 0x9: + case 0xb: + /* domain fault, accessing something we shouldn't */ + if(user){ + char buf[ERRMAX]; + + snprint(buf, sizeof buf, + "sys: access violation: pc %#lux va %#p\n", + ureg->pc, va); + postnote(up, 1, buf, NDebug); + } else + panic("kernel access violation: pc %#lux va %#p", + ureg->pc, va); + break; + case 0xd: + case 0xf: + /* permission error, copy on write or real permission error */ + faultarm(ureg, va, user, !writetomem(inst)); + break; + } +} + +/* + * here on all exceptions other than syscall (SWI) and reset + */ +void +trap(Ureg *ureg) +{ + int clockintr, user, rem; + uintptr va, ifsr; + + splhi(); /* paranoia */ + if(up != nil) + rem = ((char*)ureg)-up->kstack; + else + rem = ((char*)ureg)-((char*)m+sizeof(Mach)); + if(rem < 1024) { + iprint("trap: %d stack bytes left, up %#p ureg %#p m %#p cpu%d at pc %#lux\n", + rem, up, ureg, m, m->machno, ureg->pc); + dumpstackwithureg(ureg); + panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux", + rem, up, ureg, ureg->pc); + } + + m->perf.intrts = perfticks(); + user = (ureg->psr & PsrMask) == PsrMusr; + if(user){ + up->dbgreg = ureg; + cycles(&up->kentry); + } + + /* + * All interrupts/exceptions should be resumed at ureg->pc-4, + * except for Data Abort which resumes at ureg->pc-8. + */ + if(ureg->type == (PsrMabt+1)) + ureg->pc -= 8; + else + ureg->pc -= 4; + + clockintr = 0; /* if set, may call sched() before return */ + switch(ureg->type){ + default: + panic("unknown trap; type %#lux, psr mode %#lux", ureg->type, + ureg->psr & PsrMask); + break; + case PsrMirq: + m->intr++; + clockintr = irq(ureg); + if(0 && up && !clockintr) + preempted(); /* this causes spurious suicides */ + break; + case PsrMabt: /* prefetch (instruction) fault */ + va = ureg->pc; + ifsr = ifsrget(); + ifsr = (ifsr>>7) & 0x8 | ifsr & 0x7; + switch(ifsr){ + case 0x02: /* instruction debug event (BKPT) */ + if(user) + postnote(up, 1, "sys: breakpoint", NDebug); + else{ + iprint("kernel bkpt: pc %#lux inst %#ux\n", + va, *(u32int*)va); + panic("kernel bkpt"); + } + break; + default: + faultarm(ureg, va, user, 1); + break; + } + break; + case PsrMabt+1: /* data fault */ + datafault(ureg, user); + break; + case PsrMund: /* undefined instruction */ + if(!user) { + if (ureg->pc & 3) { + iprint("rounding fault pc %#lux down to word\n", + ureg->pc); + ureg->pc &= ~3; + } + if (Debug) + iprint("mathemu: cpu%d fpon %d instr %#8.8lux at %#p\n", + m->machno, m->fpon, *(ulong *)ureg->pc, + ureg->pc); + dumpstackwithureg(ureg); + panic("cpu%d: undefined instruction: pc %#lux inst %#ux", + m->machno, ureg->pc, ((u32int*)ureg->pc)[0]); + } else if(seg(up, ureg->pc, 0) != nil && + *(u32int*)ureg->pc == 0xD1200070) + postnote(up, 1, "sys: breakpoint", NDebug); + else if(fpuemu(ureg) == 0){ /* didn't find any FP instrs? */ + char buf[ERRMAX]; + + snprint(buf, sizeof buf, + "undefined instruction: pc %#lux instr %#8.8lux\n", + ureg->pc, *(ulong *)ureg->pc); + postnote(up, 1, buf, NDebug); + } + break; + } + splhi(); + + /* delaysched set because we held a lock or because our quantum ended */ + if(up && up->delaysched && clockintr){ + sched(); /* can cause more traps */ + splhi(); + } + + if(user){ + if(up->procctl || up->nnote) + notify(ureg); + kexit(ureg); + } +} + +/* + * Fill in enough of Ureg to get a stack trace, and call a function. + * Used by debugging interface rdb. + */ +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + + memset(&ureg, 0, sizeof ureg); + ureg.pc = getcallerpc(&fn); + ureg.sp = PTR2UINT(&fn); + fn(&ureg); +} + +static void +dumpstackwithureg(Ureg *ureg) +{ + int x; + uintptr l, v, i, estack; + char *s; + + dumpregs(ureg); + if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){ + iprint("dumpstack disabled\n"); + return; + } + delay(1000); + iprint("dumpstack\n"); + + x = 0; + x += iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n", + ureg->pc, ureg->sp, ureg->r14); + delay(20); + i = 0; + if(up + && (uintptr)&l >= (uintptr)up->kstack + && (uintptr)&l <= (uintptr)up->kstack+KSTACK) + estack = (uintptr)up->kstack+KSTACK; + else if((uintptr)&l >= (uintptr)m->stack + && (uintptr)&l <= (uintptr)m+MACHSIZE) + estack = (uintptr)m+MACHSIZE; + else + return; + x += iprint("estackx %p\n", estack); + + for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){ + v = *(uintptr*)l; + if((KTZERO < v && v < (uintptr)etext) || estack-l < 32){ + x += iprint("%.8p ", v); + delay(20); + i++; + } + if(i == 8){ + i = 0; + x += iprint("\n"); + delay(20); + } + } + if(i) + iprint("\n"); + delay(3000); +} + +void +dumpstack(void) +{ + callwithureg(dumpstackwithureg); +} + +/* + * dump general registers + */ +static void +dumpgpr(Ureg* ureg) +{ + if(up != nil) + iprint("cpu%d: registers for %s %lud\n", + m->machno, up->text, up->pid); + else + iprint("cpu%d: registers for kernel\n", m->machno); + + delay(20); + iprint("%#8.8lux\tr0\n", ureg->r0); + iprint("%#8.8lux\tr1\n", ureg->r1); + iprint("%#8.8lux\tr2\n", ureg->r2); + delay(20); + iprint("%#8.8lux\tr3\n", ureg->r3); + iprint("%#8.8lux\tr4\n", ureg->r4); + iprint("%#8.8lux\tr5\n", ureg->r5); + delay(20); + iprint("%#8.8lux\tr6\n", ureg->r6); + iprint("%#8.8lux\tr7\n", ureg->r7); + iprint("%#8.8lux\tr8\n", ureg->r8); + delay(20); + iprint("%#8.8lux\tr9 (up)\n", ureg->r9); + iprint("%#8.8lux\tr10 (m)\n", ureg->r10); + iprint("%#8.8lux\tr11 (loader temporary)\n", ureg->r11); + iprint("%#8.8lux\tr12 (SB)\n", ureg->r12); + delay(20); + iprint("%#8.8lux\tr13 (sp)\n", ureg->r13); + iprint("%#8.8lux\tr14 (link)\n", ureg->r14); + iprint("%#8.8lux\tr15 (pc)\n", ureg->pc); + delay(20); + iprint("%10.10lud\ttype\n", ureg->type); + iprint("%#8.8lux\tpsr\n", ureg->psr); + delay(500); +} + +void +dumpregs(Ureg* ureg) +{ + dumpgpr(ureg); +} + +void +fiq(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 Sun Apr 11 00:00:00 2021 @@ -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, @@ -73,56 +49,11 @@ .regs = (u32int*)AUXREGS, .name = "uart0", .freq = 250000000, + .baud = 115200, .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<baud); if(ie){ intrenable(IRQaux, interrupt, uart, 0, "uart"); ap[MuIer] = RxIen|TxIen; @@ -385,7 +316,7 @@ if(!uart->enabled) (*uart->phys->enable)(uart, 0); - uartctl(uart, "b9600 l8 pn s1"); + uartctl(uart, "l8 pn s1"); if(*cmd != '\0') uartctl(uart, cmd); @@ -417,8 +348,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/usb.h /sys/src/9/bcm/usb.h --- /n/sources/plan9/sys/src/9/bcm/usb.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/usb.h Sun Apr 11 00:00:00 2021 @@ -0,0 +1,205 @@ +/* + * common USB definitions. + */ +#define dprint if(debug)print +#define ddprint if(debug>1)print +#define deprint if(debug || ep->debug)print +#define ddeprint if(debug>1 || ep->debug>1)print + +#define GET2(p) ((((p)[1]&0xFF)<<8)|((p)[0]&0xFF)) +#define PUT2(p,v) {((p)[0] = (v)); ((p)[1] = (v)>>8);} + +typedef struct Udev Udev; /* USB device */ +typedef struct Ep Ep; /* Endpoint */ +typedef struct Hci Hci; /* Host Controller Interface */ +typedef struct Hciimpl Hciimpl; /* Link to the controller impl. */ + +enum +{ + /* fundamental constants */ + Ndeveps = 16, /* max nb. of endpoints per device */ + + /* tunable parameters */ + Nhcis = 16, /* max nb. of HCIs */ + Neps = 128, /* max nb. of endpoints */ + Maxctllen = 32*1024, /* max allowed sized for ctl. xfers; see Maxdevconf */ + Xfertmout = 2000, /* default request time out (ms) */ + + /* transfer types. keep this order */ + Tnone = 0, /* no tranfer type configured */ + Tctl, /* wr req + rd/wr data + wr/rd sts */ + Tiso, /* stream rd or wr (real time) */ + Tbulk, /* stream rd or wr */ + Tintr, /* msg rd or wr */ + Nttypes, /* number of transfer types */ + + Epmax = 0xF, /* max ep. addr */ + Devmax = 0x7F, /* max dev. addr */ + + /* Speeds */ + Fullspeed = 0, + Lowspeed, + Highspeed, + Nospeed, + Superspeed, + + /* request type */ + Rh2d = 0<<7, + Rd2h = 1<<7, + Rstd = 0<<5, + Rclass = 1<<5, + Rdev = 0, + Rep = 2, + Rother = 3, + + /* req offsets */ + Rtype = 0, + Rreq = 1, + Rvalue = 2, + Rindex = 4, + Rcount = 6, + Rsetuplen = 8, + + /* standard requests */ + Rgetstatus = 0, + Rclearfeature = 1, + Rsetfeature = 3, + Rsetaddr = 5, + Rgetdesc = 6, + + /* device states */ + Dconfig = 0, /* configuration in progress */ + Denabled, /* address assigned */ + Ddetach, /* device is detached */ + Dreset, /* its port is being reset */ + + /* (root) Hub reply to port status (reported to usbd) */ + HPpresent = 0x1, + HPenable = 0x2, + HPsuspend = 0x4, + HPovercurrent = 0x8, + HPreset = 0x10, + HPpower = 0x100, + HPslow = 0x200, + HPhigh = 0x400, + HPstatuschg = 0x10000, + HPchange = 0x20000, +}; + +/* + * Services provided by the driver. + * epopen allocates hardware structures to prepare the endpoint + * for I/O. This happens when the user opens the data file. + * epclose releases them. This happens when the data file is closed. + * epwrite tries to write the given bytes, waiting until all of them + * have been written (or failed) before returning; but not for Iso. + * epread does the same for reading. + * It can be assumed that endpoints are DMEXCL but concurrent + * read/writes may be issued and the controller must take care. + * For control endpoints, device-to-host requests must be followed by + * a read of the expected length if needed. + * The port requests are called when usbd issues commands for root + * hubs. Port status must return bits as a hub request would do. + * Toggle handling and other details are left for the controller driver + * to avoid mixing too much the controller and the comon device. + * While an endpoint is closed, its toggles are saved in the Ep struct. + */ +struct Hciimpl +{ + void *aux; /* for controller info */ + void (*init)(Hci*); /* init. controller */ + void (*dump)(Hci*); /* debug */ + void (*interrupt)(Ureg*, void*); /* service interrupt */ + void (*epopen)(Ep*); /* prepare ep. for I/O */ + void (*epclose)(Ep*); /* terminate I/O on ep. */ + long (*epread)(Ep*,void*,long); /* transmit data for ep */ + long (*epwrite)(Ep*,void*,long); /* receive data for ep */ + char* (*seprintep)(char*,char*,Ep*); /* debug */ + int (*portenable)(Hci*, int, int); /* enable/disable port */ + int (*portreset)(Hci*, int, int); /* set/clear port reset */ + int (*portstatus)(Hci*, int); /* get port status */ + void (*shutdown)(Hci*); /* shutdown for reboot */ + void (*debug)(Hci*, int); /* set/clear debug flag */ +}; + +struct Hci +{ + ISAConf; /* hardware info */ + int tbdf; /* type+busno+devno+funcno */ + int ctlrno; /* controller number */ + int nports; /* number of ports in hub */ + int highspeed; + uint superspeed; /* bitmap of superspeed ports */ + Hciimpl; /* HCI driver */ +}; + +/* + * USB endpoint. + * All endpoints are kept in a global array. The first + * block of fields is constant after endpoint creation. + * The rest is configuration information given to all controllers. + * The first endpoint for a device (known as ep0) represents the + * device and is used to configure it and create other endpoints. + * Its QLock also protects per-device data in dev. + * See Hciimpl for clues regarding how this is used by controllers. + */ +struct Ep +{ + Ref; /* one per fid (and per dev ep for ep0s) */ + + /* const once inited. */ + int idx; /* index in global eps array */ + int nb; /* endpoint number in device */ + Hci* hp; /* HCI it belongs to */ + Udev* dev; /* device for the endpoint */ + Ep* ep0; /* control endpoint for its device */ + + QLock; /* protect fields below */ + char* name; /* for ep file names at #u/ */ + int inuse; /* endpoint is open */ + int mode; /* OREAD, OWRITE, or ORDWR */ + int clrhalt; /* true if halt was cleared on ep. */ + int debug; /* per endpoint debug flag */ + char* info; /* for humans to read */ + long maxpkt; /* maximum packet size */ + int ttype; /* tranfer type */ + ulong load; /* in µs, for a fransfer of maxpkt bytes */ + void* aux; /* for controller specific info */ + u64int rhrepl; /* fake root hub replies */ + int toggle[2]; /* saved toggles (while ep is not in use) */ + long pollival; /* poll interval ([µ]frames; intr/iso) */ + long hz; /* poll frequency (iso) */ + long samplesz; /* sample size (iso) */ + int ntds; /* nb. of Tds per µframe */ + int tmout; /* 0 or timeout for transfers (ms) */ +}; + +/* + * Per-device configuration and cached list of endpoints. + * eps[0]->QLock protects it. + */ +struct Udev +{ + int nb; /* USB device number */ + int state; /* state for the device */ + int ishub; /* hubs can allocate devices */ + int isroot; /* is a root hub */ + int speed; /* Full/Low/High/No -speed */ + int hub; /* dev number for the parent hub */ + int port; /* port number in the parent hub */ + Ep* eps[Ndeveps]; /* end points for this device (cached) */ + int addr; /* device address */ + int depth; /* hub depth from root port */ + int rootport; /* port number on root hub */ + int routestr; /* route string */ + + void *aux; + void (*free)(void*); + +}; + +void addhcitype(char *type, int (*reset)(Hci*)); + +extern char *usbmodename[]; + +extern char *seprintdata(char*,char*,uchar*,int); 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 Sun Apr 11 00:00:00 2021 @@ -20,7 +20,7 @@ #include "fns.h" #include "io.h" #include "../port/error.h" -#include "../port/usb.h" +#include "usb.h" #include "dwcotg.h" @@ -33,12 +33,25 @@ Read = 0, Write = 1, + + /* + * Workaround for an unexplained glitch where an Ack interrupt + * is received without Chhltd, whereupon all channels remain + * permanently busy and can't be halted. This was only seen + * when the controller is reading a sequence of bulk input + * packets in DMA mode. Setting Slowbulkin=1 will avoid the + * lockup by reading packets individually with an interrupt + * after each. More recent chips don't seem to exhibit the + * problem, so it's probably safe to leave this off now. + */ + Slowbulkin = 0, }; typedef struct Ctlr Ctlr; typedef struct Epio Epio; struct Ctlr { + Lock; Dwcregs *regs; /* controller registers */ int nchan; /* number of host channels */ ulong chanbusy; /* bitmap of in-use channels */ @@ -52,7 +65,11 @@ }; struct Epio { - QLock; + union { + QLock rlock; + QLock ctllock; + }; + QLock wlock; Block *cb; ulong lastpoll; }; @@ -65,13 +82,31 @@ 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) { Ctlr *ctlr; int bitmap, i; + static int first; ctlr = ep->hp->aux; +retry: qlock(&ctlr->chanlock); bitmap = ctlr->chanbusy; for(i = 0; i < ctlr->nchan; i++) @@ -81,8 +116,10 @@ return &ctlr->regs->hchan[i]; } qunlock(&ctlr->chanlock); - panic("miller is a lazy git"); - return nil; + if(!first++) + print("usbdwc: all host channels busy - retrying\n"); + tsleep(&up->sleep, return0, 0, 1); + goto retry; } static void @@ -157,23 +194,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 +227,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 +235,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 +254,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 +291,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 +386,7 @@ else n = len; hc->hctsiz = n | npkt<hcdma = PADDR(a); + hc->hcdma = dmaaddr(a); nleft = len; logstart(ep); @@ -377,13 +417,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){ @@ -404,12 +450,12 @@ continue; } logdump(ep); - print("usbotg: ep%d.%d error intr %8.8ux\n", + print("usbdwc: ep%d.%d error intr %8.8ux\n", ep->dev->nb, ep->nb, i); if(i & ~(Chhltd|Ack)) error(Eio); if(hc->hcdma != hcdma) - print("usbotg: weird hcdma %x->%x intr %x->%x\n", + print("usbdwc: weird hcdma %ux->%ux intr %ux->%ux\n", hcdma, hc->hcdma, i, hc->hcint); } n = hc->hcdma - hcdma; @@ -419,13 +465,13 @@ 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)) n = nt; else - print("usbotg: intr %8.8ux " + print("usbdwc: intr %8.8ux " "dma %8.8ux-%8.8ux " "hctsiz %8.8ux-%8.ux\n", i, hcdma, hc->hcdma, hctsiz, @@ -490,7 +536,7 @@ nexterror(); } chansetup(hc, ep); - if(rw == Read && ep->ttype == Tbulk) + if(Slowbulkin && rw == Read && ep->ttype == Tbulk) n = multitrans(ep, hc, rw, a, n); else{ n = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw], @@ -523,8 +569,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 +595,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) @@ -626,7 +673,7 @@ greset(r, Rxfflsh); r->grstctl = TXF_ALL; greset(r, Txfflsh); - dprint("usbotg: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n", + dprint("usbdwc: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n", n, r->grxfsiz, r->gnptxfsiz, r->hptxfsiz); r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng; @@ -653,6 +700,7 @@ ctlr = hp->aux; r = ctlr->regs; wakechan = 0; + filock(ctlr); intr = r->gintsts; if(intr & Hcintr){ haint = r->haint & r->haintmsk; @@ -678,6 +726,7 @@ ctlr->wakechan |= wakechan; armtimerset(1); } + fiunlock(ctlr); } static void @@ -685,14 +734,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]); @@ -703,7 +752,7 @@ static void epopen(Ep *ep) { - ddprint("usbotg: epopen ep%d.%d ttype %d\n", + ddprint("usbdwc: epopen ep%d.%d ttype %d\n", ep->dev->nb, ep->nb, ep->ttype); switch(ep->ttype){ case Tnone: @@ -726,7 +775,7 @@ static void epclose(Ep *ep) { - ddprint("usbotg: epclose ep%d.%d ttype %d\n", + ddprint("usbdwc: epclose ep%d.%d ttype %d\n", ep->dev->nb, ep->nb, ep->ttype); switch(ep->ttype){ case Tctl: @@ -742,6 +791,7 @@ epread(Ep *ep, void *a, long n) { Epio *epio; + QLock *q; Block *b; uchar *p; ulong elapsed; @@ -749,10 +799,11 @@ ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n); epio = ep->aux; + q = ep->ttype == Tctl? &epio->ctllock : &epio->rlock; b = nil; - qlock(epio); + qlock(q); if(waserror()){ - qunlock(epio); + qunlock(q); if(b) freeb(b); nexterror(); @@ -762,7 +813,7 @@ error(Egreg); case Tctl: nr = ctldata(ep, a, n); - qunlock(epio); + qunlock(q); poperror(); return nr; case Tintr: @@ -772,13 +823,15 @@ /* 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); + qunlock(q); freeb(b); poperror(); return nr; @@ -789,16 +842,18 @@ epwrite(Ep *ep, void *a, long n) { Epio *epio; + QLock *q; Block *b; uchar *p; ulong elapsed; ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n); epio = ep->aux; + q = ep->ttype == Tctl? &epio->ctllock : &epio->wlock; b = nil; - qlock(epio); + qlock(q); if(waserror()){ - qunlock(epio); + qunlock(q); if(b) freeb(b); nexterror(); @@ -814,8 +869,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) @@ -824,7 +880,7 @@ n = eptrans(ep, Write, p, n); epio->lastpoll = TK2MS(m->ticks); } - qunlock(epio); + qunlock(q); freeb(b); poperror(); return n; @@ -846,11 +902,11 @@ assert(port == 1); ctlr = hp->aux; r = ctlr->regs; - dprint("usbotg enable=%d; sts %#x\n", on, r->hport0); + dprint("usbdwc enable=%d; sts %#x\n", on, r->hport0); if(!on) r->hport0 = Prtpwr | Prtena; tsleep(&up->sleep, return0, 0, Enabledelay); - dprint("usbotg enable=%d; sts %#x\n", on, r->hport0); + dprint("usbdwc enable=%d; sts %#x\n", on, r->hport0); return 0; } @@ -864,7 +920,7 @@ assert(port == 1); ctlr = hp->aux; r = ctlr->regs; - dprint("usbotg reset=%d; sts %#x\n", on, r->hport0); + dprint("usbdwc reset=%d; sts %#x\n", on, r->hport0); if(!on) return 0; r->hport0 = Prtpwr | Prtrst; @@ -875,9 +931,9 @@ b = s & (Prtconndet|Prtenchng|Prtovrcurrchng); if(b != 0) r->hport0 = Prtpwr | b; - dprint("usbotg reset=%d; sts %#x\n", on, s); + dprint("usbdwc reset=%d; sts %#x\n", on, s); if((s & Prtena) == 0) - print("usbotg: host port not enabled after reset"); + print("usbdwc: host port not enabled after reset"); return 0; } @@ -947,7 +1003,7 @@ id = ctlr->regs->gsnpsid; if((id>>16) != ('O'<<8 | 'T')) return -1; - dprint("usbotg: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF); + dprint("usbdwc: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF); intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc"); diff -Nru /n/sources/plan9/sys/src/9/bcm/usbxhci.c /sys/src/9/bcm/usbxhci.c --- /n/sources/plan9/sys/src/9/bcm/usbxhci.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/usbxhci.c Sun Apr 11 00:00:00 2021 @@ -0,0 +1,1837 @@ +/* + * from 9front ../port/usbxhci.c + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "pci.h" +#include "../port/error.h" +#include "usb.h" + +extern void dmaflush(int, void*, ulong); + +enum { + /* Capability Registers */ + CAPLENGTH = 0x00/4, // 1 + HCIVERSION = 0x02/4, // 2 + HCSPARAMS1 = 0x04/4, + HCSPARAMS2 = 0x08/4, + HCSPARAMS3 = 0x0C/4, + + HCCPARAMS = 0x10/4, + AC64 = 1<<0, + BNC = 1<<1, + CSZ = 1<<2, + PPC = 1<<3, + PIND = 1<<4, + LHRC = 1<<5, + LTC = 1<<6, + NSS = 1<<7, + + DBOFF = 0x14/4, + RTSOFF = 0x18/4, + + HCCPARAMS2 = 0x1C/4, + + /* Operational Registers */ + USBCMD = 0x00/4, /* USB Command Register */ + RUNSTOP = 1<<0, /* Run/Stop - RW */ + HCRST = 1<<1, /* Host Controller Reset - RW */ + INTE = 1<<2, /* Interrupter Enable - RW */ + HSEE = 1<<3, /* Host System Error Enable - RW */ + LHCRST = 1<<7, /* Light Host Controller Reset - RO/RW */ + CSS = 1<<8, /* Controller Save State - RW */ + CRS = 1<<9, /* Controller Restore State - RW */ + EWE = 1<<10, /* Enable Wrap Event - RW */ + EU3S = 1<<11, /* Enable U3 MFINDEX Stop - RW */ + + USBSTS = 0x04/4, /* USB Status Register */ + HCH = 1<<0, /* HCHalted - RO */ + HSE = 1<<2, /* Host System Error - RW1C */ + EINT = 1<<3, /* Event Interrupt - RW1C */ + PCD = 1<<4, /* Port Change Detect - RW1C */ + SSS = 1<<8, /* Save State Status - RO */ + RSS = 1<<9, /* Restore State Status - RO */ + SRE = 1<<10, /* Save/Restore Error - RW1C */ + CNR = 1<<11, /* Controller Not Ready - RO */ + HCE = 1<<12, /* Host Controller Error - RO */ + + PAGESIZE = 0x08/4, /* Page Size - RO */ + + DNCTRL = 0x14/4, /* Device Notification Control Register - RW */ + + CRCR = 0x18/4, /* Command Ring Control Register - RW */ + RCS = 1<<0, /* Ring Cycle State - RW */ + CS = 1<<1, /* Command Stop - RW1S */ + CA = 1<<2, /* Command Abort - RW1S */ + CRR = 1<<3, /* Command Ring Running - RO */ + + DCBAAP = 0x30/4, // 8 + + CONFIG = 0x38/4, /* Configure Register (MaxSlotEn[7:0]) */ + + /* Port Register Set */ + PORTSC = 0x00/4, /* Port status and Control Register */ + CCS = 1<<0, /* Current Connect Status - ROS */ + PED = 1<<1, /* Port Enable/Disabled - RW1CS */ + OCA = 1<<3, /* Over-current Active - RO */ + PR = 1<<4, /* Port Reset - RW1S */ + PLS = 15<<5, /* Port Link State - RWS */ + PP = 1<<9, /* Port Power - RWS */ + PS = 15<<10, /* Port Speed - ROS */ + PIC = 3<<14, /* Port Indicator Control - RWS */ + LWS = 1<<16, /* Port Link Write Strobe - RW */ + CSC = 1<<17, /* Connect Status Change - RW1CS */ + PEC = 1<<18, /* Port Enabled/Disabled Change - RW1CS */ + WRC = 1<<19, /* Warm Port Reset Change - RW1CS */ + OCC = 1<<20, /* Over-current Change - RW1CS */ + PRC = 1<<21, /* Port Reset Change - RW1CS */ + PLC = 1<<22, /* Port Link State Change - RW1CS */ + CEC = 1<<23, /* Port Config Error Change - RW1CS */ + CAS = 1<<24, /* Cold Attach Status - RO */ + WCE = 1<<25, /* Wake on Connect Enable - RWS */ + WDE = 1<<26, /* Wake on Disconnect Enable - RWS */ + WOE = 1<<27, /* Wake on Over-current Enable - RWS */ + DR = 1<<30, /* Device Removable - RO */ + WPR = 1<<31, /* Warm Port Reset - RW1S */ + + PORTPMSC = 0x04/4, + PORTLI = 0x08/4, + + /* Host Controller Runtime Register */ + MFINDEX = 0x0000/4, /* Microframe Index */ + IR0 = 0x0020/4, /* Interrupt Register Set 0 */ + + /* Interrupter Registers */ + IMAN = 0x00/4, /* Interrupter Management */ + IMOD = 0x04/4, /* Interrupter Moderation */ + ERSTSZ = 0x08/4, /* Event Ring Segment Table Size */ + ERSTBA = 0x10/4, /* Event Ring Segment Table Base Address */ + ERDP = 0x18/4, /* Event Ring Dequeue Pointer */ + + /* TRB flags */ + TR_ENT = 1<<1, + TR_ISP = 1<<2, + TR_NS = 1<<3, + TR_CH = 1<<4, + TR_IOC = 1<<5, + TR_IDT = 1<<6, + TR_BEI = 1<<9, + + /* TRB types */ + TR_RESERVED = 0<<10, + TR_NORMAL = 1<<10, + TR_SETUPSTAGE = 2<<10, + TR_DATASTAGE = 3<<10, + TR_STATUSSTAGE = 4<<10, + TR_ISOCH = 5<<10, + TR_LINK = 6<<10, + TR_EVENTDATA = 7<<10, + TR_NOOP = 8<<10, + + CR_ENABLESLOT = 9<<10, + CR_DISABLESLOT = 10<<10, + CR_ADDRESSDEV = 11<<10, + CR_CONFIGEP = 12<<10, + CR_EVALCTX = 13<<10, + CR_RESETEP = 14<<10, + CR_STOPEP = 15<<10, + CR_SETTRDQP = 16<<10, + CR_RESETDEV = 17<<10, + CR_FORCECMD = 18<<10, + CR_NEGBW = 19<<10, + CR_SETLAT = 20<<10, + CR_GETPORTBW = 21<<10, + CR_FORCEHDR = 22<<10, + CR_NOOP = 23<<10, + + ER_TRANSFER = 32<<10, + ER_CMDCOMPL = 33<<10, + ER_PORTSC = 34<<10, + ER_BWREQ = 35<<10, + ER_DOORBELL = 36<<10, + ER_HCE = 37<<10, + ER_DEVNOTE = 38<<10, + ER_MFINDEXWRAP = 39<<10, +}; + +typedef struct Ctlr Ctlr; +typedef struct Wait Wait; +typedef struct Ring Ring; +typedef struct Slot Slot; +typedef struct Epio Epio; +typedef struct Port Port; + +struct Wait +{ + Wait *next; + Ring *ring; + u32int *td; + u32int er[4]; + Rendez *z; +}; + +struct Ring +{ + int id; + + Slot *slot; + + u32int *base; + + u32int mask; + u32int shift; + + u32int rp; + u32int wp; + + u32int *ctx; + u32int *doorbell; + + int stopped; + + Wait *pending; + Lock; +}; + +struct Slot +{ + int id; + + int confval; // bConfigurationValue of SET_CONFIGURATION + int iface; // bInterfaceNumber of SET_INTERFACE + int altc; // bAlternateSetting of SET_INTERFACE + + Ctlr *ctlr; + Udev *dev; + + u32int *ibase; + u32int *obase; + + /* endpoint rings */ + int nep; + Ring epr[32]; +}; + +struct Port +{ + char spec[4]; + int proto; + + u32int *reg; +}; + +struct Ctlr +{ + Pcidev *pcidev; + + u32int *mmio; + + u32int *opr; /* operational registers */ + u32int *rts; /* runtime registers */ + u32int *dba; /* doorbell array */ + + u64int *dcba; /* device context base array */ + + u64int *sba; /* scratchpad buffer array */ + void *sbp; /* scratchpad buffer pages */ + + u32int *erst[1]; /* event ring segment table */ + Ring er[1]; /* event ring segment */ + Ring cr[1]; /* command ring segment */ + QLock cmdlock; + + u32int µframe; + + QLock slotlock; + Slot **slot; /* slots by slot id */ + Port *port; + + u32int hccparams; + + int csz; + int pagesize; + int nscratch; + int nintrs; + int nslots; + + Rendez recover; + void *active; + uintptr base; +}; + +struct Epio +{ + QLock; + + Ring *ring; + Block *b; + + /* iso */ + u32int frame; + u32int period; + u32int incr; + u32int tdsz; + + int nleft; +}; + +static char Ebadlen[] = "bad usb request length"; +static char Enotconfig[] = "usb endpoint not configured"; +static char Erecover[] = "xhci controller needs reset"; + +static char* +ctlrcmd(Ctlr *ctlr, u32int c, u32int s, u64int p, u32int *er); + +static void +setrptr(u32int *reg, u64int pa) +{ + coherence(); + reg[0] = pa; + reg[1] = pa>>32; +} + +static u32int +µframe(Ctlr *ctlr) +{ + u32int µ; + do { + µ = (ctlr->rts[MFINDEX] & (1<<14)-1) | + (ctlr->µframe & ~((1<<14)-1)); + } while((int)(µ - ctlr->µframe) < 0); + return µ; +} + +static void +freering(Ring *r) +{ + if(r == nil) + return; + if(r->base != nil){ + dmaflush(0, r->base, 4*4<shift); + free(r->base); + } + memset(r, 0, sizeof(*r)); +} + +static Ring* +initring(Ring *r, int shift) +{ + r->id = 0; + r->ctx = nil; + r->slot = nil; + r->doorbell = nil; + r->pending = nil; + r->stopped = 0; + r->shift = shift; + r->mask = (1<rp = r->wp = 0; + r->base = mallocalign(4*4<base == nil){ + freering(r); + error(Enomem); + } + dmaflush(1, r->base, 4*4<pending) != nil){ + r->pending = w->next; + w->next = nil; + if((z = w->z) != nil){ + w->z = nil; + wakeup(z); + } + } +} + +static u64int +resetring(Ring *r) +{ + u64int pa; + + ilock(r); + flushring(r); + r->rp = r->wp; + pa = PCIWADDR(&r->base[4*(r->wp & r->mask)]) | ((~r->wp>>r->shift) & 1); + iunlock(r); + + return pa; +} + +static u32int* +xecp(Ctlr *ctlr, uchar id, u32int *p) +{ + u32int x, *e; + + e = &ctlr->mmio[ctlr->pcidev->mem[0].size/4]; + if(p == nil){ + p = ctlr->mmio; + x = ctlr->hccparams>>16; + } else { + assert(p < e); + x = (*p>>8) & 255; + } + while(x != 0){ + p += x; + if(p >= e) + break; + x = *p; + if((x & 255) == id) + return p; + x >>= 8; + x &= 255; + } + return nil; +} + +static void +handoff(Ctlr *ctlr) +{ + u32int *r; + int i; + + if((r = xecp(ctlr, 1, nil)) == nil) + return; + if(getconf("*noxhcihandoff") == nil){ + r[0] |= 1<<24; /* request ownership */ + for(i = 0; (r[0] & (1<<16)) != 0 && i<100; i++) + tsleep(&up->sleep, return0, nil, 10); + } + /* disable SMI interrupts */ + r[1] &= 7<<1 | 255<<5 | 7<<17 | 7<<29; + + /* clear BIOS ownership in case of timeout */ + r[0] &= ~(1<<16); +} + +static void +shutdown(Hci *hp) +{ + Ctlr *ctlr = hp->aux; + int i; + + ctlr->opr[USBCMD] = 0; + for(i=0; (ctlr->opr[USBSTS] & HCH) == 0 && i < 10; i++) + delay(10); + pciintrdisable(ctlr->pcidev->tbdf, hp->interrupt, hp); + pcidisable(ctlr->pcidev); +} + +static void +release(Ctlr *ctlr) +{ + int i; + + freering(ctlr->cr); + for(i=0; ier); i++){ + freering(&ctlr->er[i]); + free(ctlr->erst[i]); + ctlr->erst[i] = nil; + } + free(ctlr->port), ctlr->port = nil; + free(ctlr->slot), ctlr->slot = nil; + free(ctlr->dcba), ctlr->dcba = nil; + free(ctlr->sba), ctlr->sba = nil; + if(ctlr->sbp != nil){ + dmaflush(0, ctlr->sbp, ctlr->nscratch*ctlr->pagesize); + free(ctlr->sbp); + ctlr->sbp = nil; + } +} + +static void recover(void *arg); + +static void +init(Hci *hp) +{ + Ctlr *ctlr; + Port *pp; + u32int *x; + uchar *p; + int i, j; + + ctlr = hp->aux; + pcienable(ctlr->pcidev); + if(ctlr->mmio[CAPLENGTH] == -1){ + pcidisable(ctlr->pcidev); + error("controller vanished"); + } + + ctlr->opr = &ctlr->mmio[(ctlr->mmio[CAPLENGTH]&0xFF)/4]; + ctlr->dba = &ctlr->mmio[ctlr->mmio[DBOFF]/4]; + ctlr->rts = &ctlr->mmio[ctlr->mmio[RTSOFF]/4]; + + ctlr->hccparams = ctlr->mmio[HCCPARAMS]; + handoff(ctlr); + xhcireset(BUSBNO(hp->tbdf)<<20 | BUSDNO(hp->tbdf)<<15 | BUSFNO(hp->tbdf)<<12); + + for(i=0; (ctlr->opr[USBSTS] & CNR) != 0 && i<100; i++) + tsleep(&up->sleep, return0, nil, 10); + if(i == 100) + print("%s: controller not ready, status %ux\n", hp->type, ctlr->opr[USBSTS]); + + ctlr->opr[USBCMD] = HCRST; + delay(1); + for(i=0; (ctlr->opr[USBSTS] & (CNR|HCH)) != HCH && i<100; i++) + tsleep(&up->sleep, return0, nil, 10); + if(i == 100) + print("%s: controller not halted, status %ux\n", hp->type, ctlr->opr[USBSTS]); + + pcisetbme(ctlr->pcidev); + pciintrenable(ctlr->pcidev->tbdf, hp->interrupt, hp); + + if(waserror()){ + shutdown(hp); + release(ctlr); + nexterror(); + } + + ctlr->csz = (ctlr->hccparams & CSZ) != 0; + ctlr->pagesize = (ctlr->opr[PAGESIZE] & 0xFFFF) << 12; + + ctlr->nscratch = (ctlr->mmio[HCSPARAMS2] >> 27) & 0x1F | (ctlr->mmio[HCSPARAMS2] >> 16) & 0x3E0; + ctlr->nintrs = (ctlr->mmio[HCSPARAMS1] >> 8) & 0x7FF; + ctlr->nslots = (ctlr->mmio[HCSPARAMS1] >> 0) & 0xFF; + + hp->highspeed = 1; + hp->superspeed = 0; + hp->nports = (ctlr->mmio[HCSPARAMS1] >> 24) & 0xFF; + ctlr->port = malloc(hp->nports * sizeof(Port)); + if(ctlr->port == nil) + error(Enomem); + for(i=0; inports; i++) + ctlr->port[i].reg = &ctlr->opr[0x400/4 + i*4]; + + x = nil; + while((x = xecp(ctlr, 2, x)) != nil){ + i = x[2]&255; + j = (x[2]>>8)&255; + while(j--){ + if(i < 1 || i > hp->nports) + break; + pp = &ctlr->port[i-1]; + pp->proto = x[0]>>16; + memmove(pp->spec, &x[1], 4); + if(memcmp(pp->spec, "USB ", 4) == 0 && pp->proto >= 0x0300) + hp->superspeed |= 1<<(i-1); + i++; + } + } + + ctlr->slot = malloc((1+ctlr->nslots)*sizeof(ctlr->slot[0])); + ctlr->dcba = mallocalign((1+ctlr->nslots)*sizeof(ctlr->dcba[0]), 64, 0, ctlr->pagesize); + if(ctlr->slot == nil || ctlr->dcba == nil) + error(Enomem); + if(ctlr->nscratch != 0){ + ctlr->sba = mallocalign(ctlr->nscratch*8, 64, 0, ctlr->pagesize); + ctlr->sbp = mallocalign(ctlr->nscratch*ctlr->pagesize, ctlr->pagesize, 0, 0); + if(ctlr->sba == nil || ctlr->sbp == nil) + error(Enomem); + for(i=0, p = ctlr->sbp; inscratch; i++, p += ctlr->pagesize){ + memset(p, 0, ctlr->pagesize); + ctlr->sba[i] = PCIWADDR(p); + } + dmaflush(1, ctlr->sbp, ctlr->nscratch*ctlr->pagesize); + dmaflush(1, ctlr->sba, ctlr->nscratch*8); + ctlr->dcba[0] = PCIWADDR(ctlr->sba); + } else { + ctlr->dcba[0] = 0; + } + for(i=1; i<=ctlr->nslots; i++) + ctlr->dcba[i] = 0; + + ctlr->opr[CONFIG] = (ctlr->opr[CONFIG] & 0xFFFFFC00) | ctlr->nslots; /* MaxSlotsEn */ + + dmaflush(1, ctlr->dcba, (1+ctlr->nslots)*sizeof(ctlr->dcba[0])); + setrptr(&ctlr->opr[DCBAAP], PCIWADDR(ctlr->dcba)); + + initring(ctlr->cr, 8); /* 256 entries */ + ctlr->cr->id = 0; + ctlr->cr->doorbell = &ctlr->dba[0]; + setrptr(&ctlr->opr[CRCR], resetring(ctlr->cr)); + + for(i=0; inintrs; i++){ + u32int *irs = &ctlr->rts[IR0 + i*8]; + + if(i >= nelem(ctlr->er)){ + irs[ERSTSZ] = 0; /* disable ring */ + irs[IMAN] = 1; + irs[IMOD] = 0; + setrptr(&irs[ERSTBA], 0); + setrptr(&irs[ERDP], 0); + continue; + } + + /* allocate and link into event ring segment table */ + initring(&ctlr->er[i], 8); /* 256 entries */ + ctlr->erst[i] = mallocalign(4*4, 64, 0, 0); + if(ctlr->erst[i] == nil) + error(Enomem); + *((u64int*)ctlr->erst[i]) = PCIWADDR(ctlr->er[i].base); + ctlr->erst[i][2] = ctlr->er[i].mask+1; + ctlr->erst[i][3] = 0; + dmaflush(1, ctlr->erst[i], 4*4); + + irs[ERSTSZ] = 1; /* just one segment */ + irs[IMAN] = 3; + irs[IMOD] = 0; + setrptr(&irs[ERSTBA], PCIWADDR(ctlr->erst[i])); + setrptr(&irs[ERDP], PCIWADDR(ctlr->er[i].base) | (1<<3)); + } + poperror(); + + ctlr->µframe = 0; + ctlr->opr[USBSTS] = ctlr->opr[USBSTS] & (HSE|EINT|PCD|SRE); + coherence(); + + ctlr->opr[USBCMD] = RUNSTOP|INTE|HSEE|EWE; + for(i=0; (ctlr->opr[USBSTS] & (CNR|HCH)) != 0 && i<100; i++) + tsleep(&up->sleep, return0, nil, 10); + + kproc("xhcirecover", recover, hp); +} + +static int +needrecover(void *arg) +{ + Ctlr *ctlr = arg; + return ctlr->er->stopped || + (ctlr->opr[USBSTS] & (HCH|HCE|HSE)) != 0; +} + +static void +recover(void *arg) +{ + Hci *hp = arg; + Ctlr *ctlr = hp->aux; + + while(waserror()) + ; + while(!needrecover(ctlr)) + tsleep(&ctlr->recover, needrecover, ctlr, 10); + shutdown(hp); + + /* + * flush all transactions and wait until all devices have + * been detached by usbd. + */ + for(;;){ + int i, j, active; + + ilock(ctlr->cr); + ctlr->cr->stopped = 1; + flushring(ctlr->cr); + iunlock(ctlr->cr); + + active = 0; + qlock(&ctlr->slotlock); + for(i=1; i<=ctlr->nslots; i++){ + Slot *slot = ctlr->slot[i]; + if(slot == nil) + continue; + active++; + for(j=0; j < slot->nep; j++){ + Ring *ring = &slot->epr[j]; + if(ring->base == nil) + continue; + ilock(ring); + ring->stopped = 1; + flushring(ring); + iunlock(ring); + } + } + qunlock(&ctlr->slotlock); + if(active == 0) + break; + + tsleep(&up->sleep, return0, nil, 100); + } + + qlock(&ctlr->slotlock); + qlock(&ctlr->cmdlock); + + release(ctlr); + if(waserror()) { + print("xhci recovery failed: %s\n", up->errstr); + } else { + init(hp); + poperror(); + } + + qunlock(&ctlr->cmdlock); + qunlock(&ctlr->slotlock); + + pexit("", 1); +} + +static void +dump(Hci *) +{ +} + +static void +queuetd(Ring *r, u32int c, u32int s, u64int p, Wait *w) +{ + u32int *td, x; + + x = r->wp++; + if((x & r->mask) == r->mask){ + td = r->base + 4*(x & r->mask); + *(u64int*)td = PCIWADDR(r->base); + td[2] = 0; + td[3] = ((~x>>r->shift)&1) | (1<<1) | TR_LINK; + dmaflush(1, td, 4*4); + x = r->wp++; + } + td = r->base + 4*(x & r->mask); + if(w != nil){ + w->er[0] = w->er[1] = w->er[2] = w->er[3] = 0; + w->ring = r; + w->td = td; + w->z = &up->sleep; + + ilock(r); + w->next = r->pending; + r->pending = w; + iunlock(r); + } + coherence(); + *(u64int*)td = p; + td[2] = s; + td[3] = ((~x>>r->shift)&1) | c; + dmaflush(1, td, 4*4); +} + +static char *ccerrtab[] = { +[2] "Data Buffer Error", +[3] "Babble Detected Error", +[4] "USB Transaction Error", +[5] "TRB Error", +[6] "Stall Error", +[7] "Resume Error", +[8] "Bandwidth Error", +[9] "No Slots Available", +[10] "Invalid Stream Type", +[11] "Slot Not Enabled", +[12] "Endpoint Not Enabled", +[13] "Short Packet", +[14] "Ring Underrun", +[15] "Ring Overrun", +[16] "VF Event Ring Full", +[17] "Parameter Error", +[18] "Bandwidth Overrun Error", +[19] "Context State Error", +[20] "No Ping Response", +[21] "Event Ring Full", +[22] "Incompatible Device", +[23] "Missed Service Error", +[24] "Command Ring Stopped", +[25] "Command Aborted", +[26] "Stopped", +[27] "Stopped - Length Invalid", +[29] "Max Exit Latency Too Large", +[31] "Isoch Buffer Overrun", +[32] "Event Lost Error", +[33] "Undefined Error", +[34] "Invalid Stream ID", +[35] "Secondary Bandwidth Error", +[36] "Split Transaction Error", +}; + +static char* +ccerrstr(u32int cc) +{ + char *s; + + if(cc == 1 || cc == 13) + return nil; + if(cc < nelem(ccerrtab) && ccerrtab[cc] != nil) + s = ccerrtab[cc]; + else + s = "???"; + return s; +} + +static int +waitdone(void *a) +{ + return ((Wait*)a)->z == nil; +} + +static char* +waittd(Ctlr *ctlr, Wait *w, int tmout) +{ + Ring *r = w->ring; + + coherence(); + *r->doorbell = r->id; + + while(waserror()){ + if(r->stopped) { + ctlr->er->stopped = 1; + wakeup(&ctlr->recover); + + /* wait for rescue */ + tmout = 0; + continue; + } + + if(r == ctlr->cr) + ctlr->opr[CRCR] |= CA; + else + ctlrcmd(ctlr, CR_STOPEP | (r->id<<16) | (r->slot->id<<24), 0, 0, nil); + r->stopped = 1; + + /* time to abort the transaction */ + tmout = 5000; + } + if(tmout > 0){ + tsleep(&up->sleep, waitdone, w, tmout); + if(!waitdone(w)) + error("timed out"); + } else { + while(!waitdone(w)) + sleep(&up->sleep, waitdone, w); + } + poperror(); + return ccerrstr(w->er[2]>>24); +} + +static char* +ctlrcmd(Ctlr *ctlr, u32int c, u32int s, u64int p, u32int *er) +{ + Wait w[1]; + char *err; + + qlock(&ctlr->cmdlock); + if(needrecover(ctlr)){ + qunlock(&ctlr->cmdlock); + return Erecover; + } + ctlr->cr->stopped = 0; + queuetd(ctlr->cr, c, s, p, w); + err = waittd(ctlr, w, 5000); + qunlock(&ctlr->cmdlock); + + if(er != nil) + memmove(er, w->er, 4*4); + + return err; +} + +static void +completering(Ring *r, u32int *er) +{ + Wait *w, **wp; + u32int *td, x; + u64int pa; + + pa = (*(u64int*)er) & ~15ULL; + ilock(r); + + for(x = r->rp; (int)(r->wp - x) > 0;){ + td = &r->base[4*(x++ & r->mask)]; + if((u64int)PCIWADDR(td) == pa){ + r->rp = x; + break; + } + } + + wp = &r->pending; + while(w = *wp){ + if((u64int)PCIWADDR(w->td) == pa){ + Rendez *z = w->z; + + memmove(w->er, er, 4*4); + *wp = w->next; + w->next = nil; + + if(z != nil){ + w->z = nil; + wakeup(z); + } + break; + } else { + wp = &w->next; + } + } + + iunlock(r); +} + +static void +interrupt(Ureg*, void *arg) +{ + Hci *hp = arg; + Ctlr *ctlr = hp->aux; + Ring *ring = ctlr->er; + Slot *slot; + u32int *irs, *td, x; + + if(ring->base == nil) + return; + + irs = &ctlr->rts[IR0]; + x = irs[IMAN]; + if(x & 1) irs[IMAN] = x & 3; + + for(x = ring->rp;; x=++ring->rp){ + td = ring->base + 4*(x & ring->mask); + dmaflush(0, td, 4*4); + + if((((x>>ring->shift)^td[3])&1) == 0) + break; + + switch(td[3] & 0xFC00){ + case ER_CMDCOMPL: + completering(ctlr->cr, td); + break; + case ER_TRANSFER: + x = td[3]>>24; + if(x == 0 || x > ctlr->nslots) + break; + slot = ctlr->slot[x]; + if(slot == nil) + break; + completering(&slot->epr[(td[3]>>16)-1&31], td); + break; + case ER_MFINDEXWRAP: + ctlr->µframe = (ctlr->rts[MFINDEX] & (1<<14)-1) | + (ctlr->µframe+(1<<14) & ~((1<<14)-1)); + break; + case ER_HCE: + iprint("xhci: host controller error: %ux %ux %ux %ux\n", + td[0], td[1], td[2], td[3]); + ctlr->er->stopped = 1; + wakeup(&ctlr->recover); + return; + case ER_PORTSC: + break; + case ER_BWREQ: + case ER_DOORBELL: + case ER_DEVNOTE: + default: + iprint("xhci: event %ud: %ux %ux %ux %ux\n", + x, td[0], td[1], td[2], td[3]); + } + } + + setrptr(&irs[ERDP], PCIWADDR(td) | (1<<3)); +} + +static void +freeslot(void *arg) +{ + Slot *slot; + + if(arg == nil) + return; + slot = arg; + if(slot->id != 0){ + Ctlr *ctlr = slot->ctlr; + qlock(&ctlr->slotlock); + if(ctlr->slot != nil && ctlr->slot[slot->id] == slot){ + ctlrcmd(ctlr, CR_DISABLESLOT | (slot->id<<24), 0, 0, nil); + dmaflush(0, slot->obase, 32*32 << ctlr->csz); + ctlr->dcba[slot->id] = 0; + dmaflush(1, &ctlr->dcba[slot->id], sizeof(ctlr->dcba[0])); + ctlr->slot[slot->id] = nil; + } + qunlock(&ctlr->slotlock); + } + freering(&slot->epr[0]); + free(slot->ibase); + free(slot->obase); + free(slot); +} + +static Slot* +allocslot(Ctlr *ctlr, Udev *dev) +{ + u32int r[4]; + Slot *slot; + char *err; + + slot = malloc(sizeof(Slot)); + if(slot == nil) + error(Enomem); + + slot->ctlr = ctlr; + slot->dev = dev; + slot->nep = 0; + slot->id = 0; + + slot->confval = 0; + slot->iface = 0; + slot->altc = 0; + + qlock(&ctlr->slotlock); + if(waserror()){ + qunlock(&ctlr->slotlock); + freeslot(slot); + nexterror(); + } + if(ctlr->slot == nil) + error(Erecover); + slot->ibase = mallocalign(32*33 << ctlr->csz, 64, 0, ctlr->pagesize); + slot->obase = mallocalign(32*32 << ctlr->csz, 64, 0, ctlr->pagesize); + if(slot->ibase == nil || slot->obase == nil) + error(Enomem); + + if((err = ctlrcmd(ctlr, CR_ENABLESLOT, 0, 0, r)) != nil) + error(err); + slot->id = r[3]>>24; + if(slot->id <= 0 || slot->id > ctlr->nslots || ctlr->slot[slot->id] != nil){ + slot->id = 0; + error("bad slot id from controller"); + } + poperror(); + + dmaflush(1, slot->obase, 32*32 << ctlr->csz); + ctlr->dcba[slot->id] = PCIWADDR(slot->obase); + dmaflush(1, &ctlr->dcba[slot->id], sizeof(ctlr->dcba[0])); + + ctlr->slot[slot->id] = slot; + + qunlock(&ctlr->slotlock); + + return slot; +} + +static void +setdebug(Hci *, int) +{ +} + +static void +epclose(Ep *ep) +{ + Ctlr *ctlr; + Slot *slot; + Ring *ring; + Epio *io; + + if(ep->dev->isroot) + return; + + io = ep->aux; + if(io == nil) + return; + ep->aux = nil; + + ctlr = ep->hp->aux; + slot = ep->dev->aux; + + if(ep->nb > 0 && (io[OREAD].ring != nil || io[OWRITE].ring != nil)){ + u32int *w; + + /* input control context */ + w = slot->ibase; + memset(w, 0, 32<csz); + w[1] = 1; + if((ring = io[OREAD].ring) != nil){ + w[0] |= 1 << ring->id; + if(ring->id == slot->nep) + slot->nep--; + ctlrcmd(ctlr, CR_STOPEP | (ring->id<<16) | (slot->id<<24), 0, 0, nil); + } + if((ring = io[OWRITE].ring) != nil){ + w[0] |= 1 << ring->id; + if(ring->id == slot->nep) + slot->nep--; + ctlrcmd(ctlr, CR_STOPEP | (ring->id<<16) | (slot->id<<24), 0, 0, nil); + } + + /* (input) slot context */ + w += 8<csz; + w[0] = (w[0] & ~(0x1F<<27)) | slot->nep<<27; + + /* (input) ep context */ + w += ep->nb*2*8<csz; + memset(w, 0, 2*32<csz); + + dmaflush(1, slot->ibase, 32*33 << ctlr->csz); + ctlrcmd(ctlr, CR_CONFIGEP | (slot->id<<24), 0, PCIWADDR(slot->ibase), nil); + dmaflush(0, slot->obase, 32*32 << ctlr->csz); + + freering(io[OREAD].ring); + freering(io[OWRITE].ring); + } + freeb(io[OREAD].b); + freeb(io[OWRITE].b); + free(io); +} + +static void +initepctx(u32int *w, Ring *r, Ep *ep) +{ + int ival; + + if(ep->dev->speed == Lowspeed || ep->dev->speed == Fullspeed){ + for(ival=3; ival < 11 && (1<pollival; ival++) + ; + } else { + for(ival=0; ival < 15 && (1<pollival; ival++) + ; + } + w[0] = ival<<16; + w[1] = ((ep->ttype-Tctl) | (r->id&1)<<2)<<3 | (ep->ntds-1)<<8 | ep->maxpkt<<16; + if(ep->ttype != Tiso) + w[1] |= 3<<1; + *((u64int*)&w[2]) = PCIWADDR(r->base) | 1; + w[4] = 2*ep->maxpkt; + if(ep->ttype == Tintr || ep->ttype == Tiso) + w[4] |= (ep->maxpkt*ep->ntds)<<16; +} + +static void +initisoio(Epio *io, Ep *ep) +{ + if(io->ring == nil) + return; + io->frame = 0; + io->period = ep->pollival<<3*(ep->dev->speed == Fullspeed); + io->incr = (ep->hz*io->period<<8)/8000; + io->tdsz = (io->incr+255>>8)*ep->samplesz; + io->b = allocb((io->ring->mask+1)*io->tdsz); +} + +static void +initep(Ep *ep) +{ + Epio *io; + Ctlr *ctlr; + Slot *slot; + Ring *ring; + u32int *w; + char *err; + + io = ep->aux; + ctlr = ep->hp->aux; + slot = ep->dev->aux; + + io[OREAD].ring = io[OWRITE].ring = nil; + if(ep->nb == 0){ + io[OWRITE].ring = &slot->epr[0]; + return; + } + + /* (input) control context */ + w = slot->ibase; + memset(w, 0, 32<csz); + w[1] = 1; + w[31] = slot->altc<<16 | slot->iface<<8 | slot->confval; + + if(waserror()){ + freering(io[OWRITE].ring), io[OWRITE].ring = nil; + freering(io[OREAD].ring), io[OREAD].ring = nil; + nexterror(); + } + if(ep->mode != OREAD){ + ring = initring(io[OWRITE].ring = &slot->epr[ep->nb*2-1], 8); + ring->id = ep->nb*2; + if(ring->id > slot->nep) + slot->nep = ring->id; + ring->slot = slot; + ring->doorbell = &ctlr->dba[slot->id]; + ring->ctx = &slot->obase[ring->id*8<csz]; + w[1] |= 1 << ring->id; + } + if(ep->mode != OWRITE){ + ring = initring(io[OREAD].ring = &slot->epr[ep->nb*2], 8); + ring->id = ep->nb*2+1; + if(ring->id > slot->nep) + slot->nep = ring->id; + ring->slot = slot; + ring->doorbell = &ctlr->dba[slot->id]; + ring->ctx = &slot->obase[ring->id*8<csz]; + w[1] |= 1 << ring->id; + } + + /* (input) slot context */ + w += 8<csz; + w[0] = (w[0] & ~(0x1F<<27)) | slot->nep<<27; + if(!ep->dev->ishub) + w[0] &= ~(1<<25); // MTT + + /* (input) ep context */ + w += ep->nb*2*8<csz; + if(io[OWRITE].ring != nil){ + memset(w, 0, 5*4); + initepctx(w, io[OWRITE].ring, ep); + } + + w += 8<csz; + if(io[OREAD].ring != nil){ + memset(w, 0, 5*4); + initepctx(w, io[OREAD].ring, ep); + } + + dmaflush(1, slot->ibase, 32*33 << ctlr->csz); + err = ctlrcmd(ctlr, CR_CONFIGEP | (slot->id<<24), 0, PCIWADDR(slot->ibase), nil); + dmaflush(0, slot->obase, 32*32 << ctlr->csz); + if(err != nil) + error(err); + + if(ep->ttype == Tiso){ + initisoio(io+OWRITE, ep); + initisoio(io+OREAD, ep); + } + poperror(); +} + +static int +speedid(int speed) +{ + switch(speed){ + case Fullspeed: return 1; + case Lowspeed: return 2; + case Highspeed: return 3; + case Superspeed: return 4; + } + return 0; +} + +static void +epopen(Ep *ep) +{ + Ctlr *ctlr = ep->hp->aux; + Slot *slot, *hub; + Ring *ring; + Epio *io; + Udev *dev; + char *err; + u32int *w; + int i; + + if(ep->dev->isroot) + return; + if(needrecover(ctlr)) + error(Erecover); + io = malloc(sizeof(Epio)*2); + if(io == nil) + error(Enomem); + ep->aux = io; + if(waserror()){ + epclose(ep); + nexterror(); + } + dev = ep->dev; + slot = dev->aux; + if(slot != nil && slot->dev == dev){ + initep(ep); + poperror(); + return; + } + + /* first open has to be control endpoint */ + if(ep->nb != 0) + error(Egreg); + + slot = allocslot(ctlr, dev); + if(waserror()){ + freeslot(slot); + nexterror(); + } + + /* allocate control ep 0 ring */ + ring = initring(io[OWRITE].ring = &slot->epr[0], 4); + ring->id = 1; + slot->nep = 1; + ring->slot = slot; + ring->doorbell = &ctlr->dba[slot->id]; + ring->ctx = &slot->obase[8]; + + /* (input) control context */ + w = slot->ibase; + memset(w, 0, 3*32<csz); + w[1] = 3; /* A0, A1 */ + + /* (input) slot context */ + w += 8<csz; + w[2] = w[3] = 0; + w[0] = dev->routestr | speedid(dev->speed)<<20 | + (dev->speed == Highspeed && dev->ishub != 0)<<25 | // MTT + (dev->ishub != 0)<<26 | slot->nep<<27; + w[1] = dev->rootport<<16; + + /* find the parent hub that this device is conected to */ + qlock(&ctlr->slotlock); + for(i=1; i<=ctlr->nslots; i++){ + hub = ctlr->slot[i]; + if(hub == nil || hub->dev == nil || hub->dev->aux != hub) + continue; + if(hub == slot || hub->dev == dev) + continue; + if(!hub->dev->ishub) + continue; + if(hub->dev->addr != dev->hub) + continue; + if(hub->dev->rootport != dev->rootport) + continue; + + if(dev->speed < Highspeed && hub->dev->speed == Highspeed){ + w[0] |= 1<<25; // MTT + w[2] = hub->id | dev->port<<8; + } + break; + } + qunlock(&ctlr->slotlock); + + /* (input) ep context 0 */ + w += 8<csz; + initepctx(w, io[OWRITE].ring, ep); + + dmaflush(1, slot->ibase, 32*33 << ctlr->csz); + err = ctlrcmd(ctlr, CR_ADDRESSDEV | (slot->id<<24), 0, PCIWADDR(slot->ibase), nil); + dmaflush(0, slot->obase, 32*32 << ctlr->csz); + if(err != nil) + error(err); + + /* (output) slot context */ + w = slot->obase; + + dev->addr = w[3] & 0xFF; + + dev->aux = slot; + dev->free = freeslot; + + poperror(); + poperror(); +} + +static long +isoread(Ep *, uchar *, long) +{ + error(Egreg); + return 0; +} + +static long +isowrite(Ep *ep, uchar *p, long n) +{ + uchar *s, *d; + Ctlr *ctlr; + Epio *io; + u32int i, µ; + long m; + + s = p; + io = (Epio*)ep->aux + OWRITE; + qlock(io); + if(waserror()){ + qunlock(io); + nexterror(); + } + µ = io->period; + ctlr = ep->hp->aux; + if(needrecover(ctlr)) + error(Erecover); + for(i = io->frame;; i++){ + for(;;){ + m = (int)(io->ring->wp - io->ring->rp); + if(m <= 0) + i = (80 + µframe(ctlr))/µ; + if(m < io->ring->mask) + break; + *io->ring->doorbell = io->ring->id; + tsleep(&up->sleep, return0, nil, 5); + } + m = ((io->incr + (i*io->incr&255))>>8)*ep->samplesz; + d = io->b->rp + (i&io->ring->mask)*io->tdsz; + m -= io->nleft, d += io->nleft; + if(n < m){ + memmove(d, p, n); + p += n; + io->nleft += n; + break; + } + memmove(d, p, m); + p += m, n -= m; + m += io->nleft, d -= io->nleft; + io->nleft = 0; + dmaflush(1, d, m); + queuetd(io->ring, TR_ISOCH | (i*µ/8 & 0x7ff)<<20 | TR_IOC, m, PCIWADDR(d), nil); + } + io->frame = i; + while(io->ring->rp != io->ring->wp){ + int d = (int)(i*µ - µframe(ctlr))/8; + //d -= ep->sampledelay*1000 / ep->hz; + if(d < 5) + break; + *io->ring->doorbell = io->ring->id; + tsleep(&up->sleep, return0, nil, d); + } + qunlock(io); + poperror(); + + return p - s; +} + +static char* +unstall(Ep *ep, Ring *r) +{ + char *err; + + switch(r->ctx[0]&7){ + case 2: /* halted */ + case 4: /* error */ + ep->clrhalt = 1; + } + if(ep->clrhalt){ + ep->clrhalt = 0; + err = ctlrcmd(r->slot->ctlr, CR_RESETEP | (r->id<<16) | (r->slot->id<<24), 0, 0, nil); + dmaflush(0, r->ctx, 8*4 << r->slot->ctlr->csz); + if(err != nil) + return err; + r->stopped = 1; + } + if(r->stopped){ + err = ctlrcmd(r->slot->ctlr, CR_SETTRDQP | (r->id<<16) | (r->slot->id<<24), 0, resetring(r), nil); + dmaflush(0, r->ctx, 8*4 << r->slot->ctlr->csz); + if(err != nil) + return err; + r->stopped = 0; + } + if(r->wp - r->rp >= r->mask) + return "Ring Full"; + return nil; +} + +static long +epread(Ep *ep, void *va, long n) +{ + Epio *io; + Ctlr *ctlr; + uchar *p; + char *err; + Wait w[1]; + + if(ep->dev->isroot) + error(Egreg); + + p = va; + if(ep->ttype == Tctl){ + io = (Epio*)ep->aux + OREAD; + qlock(io); + if(io->b == nil || BLEN(io->b) == 0){ + qunlock(io); + return 0; + } + if(n > BLEN(io->b)) + n = BLEN(io->b); + memmove(p, io->b->rp, n); + io->b->rp += n; + qunlock(io); + return n; + } else if(ep->ttype == Tiso) + return isoread(ep, p, n); + + if((uintptr)p <= KZERO){ + Block *b; + + b = allocb(n); + if(waserror()){ + freeb(b); + nexterror(); + } + n = epread(ep, b->rp, n); + memmove(p, b->rp, n); + freeb(b); + poperror(); + return n; + } + + ctlr = (Ctlr*)ep->hp->aux; + io = (Epio*)ep->aux + OREAD; + qlock(io); + if(waserror()){ + dmaflush(0, io->ring->ctx, 8*4 << ctlr->csz); + qunlock(io); + nexterror(); + } + + if((err = unstall(ep, io->ring)) != nil) + error(err); + + dmaflush(1, p, n); + queuetd(io->ring, TR_NORMAL | TR_IOC, n, PCIWADDR(p), w); + err = waittd(ctlr, w, ep->tmout); + dmaflush(0, p, n); + if(err != nil) + error(err); + + qunlock(io); + poperror(); + + n -= (w->er[2] & 0xFFFFFF); + if(n < 0) + n = 0; + + return n; +} + +static long +epwrite(Ep *ep, void *va, long n) +{ + Wait w[3]; + Ctlr *ctlr; + Epio *io; + uchar *p; + char *err; + + if(ep->dev->isroot) + error(Egreg); + + p = va; + if(ep->ttype == Tctl){ + int dir, len; + Ring *ring; + Slot *slot; + + if(n < 8) + error(Eshort); + + if(p[0] == 0x00 && p[1] == 0x05) + return n; + + ctlr = (Ctlr*)ep->hp->aux; + io = (Epio*)ep->aux + OREAD; + ring = io[OWRITE-OREAD].ring; + slot = ring->slot; + qlock(io); + if(waserror()){ + ilock(ring); + ring->pending = nil; + iunlock(ring); + dmaflush(0, ring->ctx, 8*4 << ctlr->csz); + qunlock(io); + nexterror(); + } + if(io->b != nil){ + freeb(io->b); + io->b = nil; + } + len = GET2(&p[6]); + dir = (p[0] & Rd2h) != 0; + if(len > 0){ + io->b = allocb(len); + if(dir == 0){ /* out */ + assert(len >= n-8); + memmove(io->b->wp, p+8, n-8); + } else { + memset(io->b->wp, 0, len); + io->b->wp += len; + } + } + if((err = unstall(ep, ring)) != nil) + error(err); + + if((ring->ctx[1]>>16) != ep->maxpkt){ + u32int *w = slot->ibase; + w[0] = 0; + w[1] = 1<id; + w += (ring->id+1)*8<csz; + initepctx(w, ring, ep); + dmaflush(1, slot->ibase, 32*33 << ctlr->csz); + err = ctlrcmd(ctlr, CR_EVALCTX | (slot->id<<24), 0, PCIWADDR(slot->ibase), nil); + dmaflush(0, slot->obase, 32*32 << ctlr->csz); + if(err != nil) + error(err); + } + + queuetd(ring, TR_SETUPSTAGE | (len > 0 ? 2+dir : 0)<<16 | TR_IDT | TR_IOC, 8, + p[0] | p[1]<<8 | GET2(&p[2])<<16 | + (u64int)(GET2(&p[4]) | len<<16)<<32, &w[0]); + if(len > 0){ + dmaflush(1, io->b->rp, len); + queuetd(ring, TR_DATASTAGE | dir<<16 | TR_IOC, len, + PCIWADDR(io->b->rp), &w[1]); + } + queuetd(ring, TR_STATUSSTAGE | (len == 0 || !dir)<<16 | TR_IOC, 0, 0, &w[2]); + + if((err = waittd(ctlr, &w[0], ep->tmout)) != nil) + error(err); + if(len > 0){ + if((err = waittd(ctlr, &w[1], ep->tmout)) != nil) + error(err); + if(dir != 0){ + dmaflush(0, io->b->rp, len); + io->b->wp -= (w[1].er[2] & 0xFFFFFF); + if(io->b->wp < io->b->rp) + io->b->wp = io->b->rp; + } + } + if((err = waittd(ctlr, &w[2], ep->tmout)) != nil) + error(err); + + if(p[0] == 0x00 && p[1] == 0x09){ + slot->confval = GET2(&p[2]); + } else if(p[0] == 0x01 && p[1] == 0x0d){ + slot->altc = GET2(&p[2]); + slot->iface = GET2(&p[4]); + } + + qunlock(io); + poperror(); + + return n; + } else if(ep->ttype == Tiso) + return isowrite(ep, p, n); + + if((uintptr)p <= KZERO){ + Block *b; + + b = allocb(n); + if(waserror()){ + freeb(b); + nexterror(); + } + memmove(b->wp, p, n); + n = epwrite(ep, b->wp, n); + freeb(b); + poperror(); + return n; + } + + ctlr = (Ctlr*)ep->hp->aux; + io = (Epio*)ep->aux + OWRITE; + qlock(io); + if(waserror()){ + dmaflush(0, io->ring->ctx, 8*4 << ctlr->csz); + qunlock(io); + nexterror(); + } + + if((err = unstall(ep, io->ring)) != nil) + error(err); + + dmaflush(1, p, n); + queuetd(io->ring, TR_NORMAL | TR_IOC, n, PCIWADDR(p), w); + if((err = waittd(ctlr, w, ep->tmout)) != nil) + error(err); + + qunlock(io); + poperror(); + + return n; +} + +static char* +seprintep(char *s, char*, Ep*) +{ + return s; +} + +static int +portstatus(Hci *hp, int port) +{ + Ctlr *ctlr = hp->aux; + u32int psc, ps; + + if(ctlr->port == nil || needrecover(ctlr)) + return 0; + + ps = 0; + psc = ctlr->port[port-1].reg[PORTSC]; + if(psc & CCS) ps |= HPpresent; + if(psc & PED) ps |= HPenable; + if(psc & OCA) ps |= HPovercurrent; + if(psc & PR) ps |= HPreset; + + if((hp->superspeed & (1<<(port-1))) != 0){ + ps |= psc & (PLS|PP); + if(psc & CSC) ps |= 1<<0+16; + if(psc & OCC) ps |= 1<<3+16; + if(psc & PRC) ps |= 1<<4+16; + if(psc & WRC) ps |= 1<<5+16; + if(psc & PLC) ps |= 1<<6+16; + if(psc & CEC) ps |= 1<<7+16; + } else { + if((ps & HPreset) == 0){ + switch((psc>>10)&15){ + case 1: + /* full speed */ + break; + case 2: + ps |= HPslow; + break; + case 3: + ps |= HPhigh; + break; + } + } + if(psc & PP) ps |= HPpower; + if(psc & CSC) ps |= HPstatuschg; + if(psc & PRC) ps |= HPchange; + } + + return ps; +} + +static int +portenable(Hci*, int, int) +{ + return 0; +} + +static int +portreset(Hci *hp, int port, int on) +{ + Ctlr *ctlr = hp->aux; + + if(ctlr->port == nil || needrecover(ctlr)) + return 0; + + if(on){ + ctlr->port[port-1].reg[PORTSC] |= PR; + tsleep(&up->sleep, return0, nil, 200); + } + return 0; +} + + +static Ctlr *ctlrs[Nhcis]; + +static void +scanpci(void) +{ + static int already = 0; + int i; + uintpci io; + Ctlr *ctlr; + Pcidev *p; + u32int *mmio; + + if(already) + return; + already = 1; + p = nil; + while ((p = pcimatch(p, 0, 0)) != nil) { + /* + * Find XHCI controllers (Programming Interface = 0x30). + */ + if(p->ccrb != Pcibcserial || p->ccru != Pciscusb || p->ccrp != 0x30) + continue; + io = p->mem[0].bar & ~0x0f; + if(io == 0) + continue; + print("usbxhci: %#x %#x: port %llux size %#x irq %d\n", + p->vid, p->did, io, p->mem[0].size, p->intl); + mmio = (u32int*)mmukmapx(VIRTPCI, io, p->mem[0].size); + if(mmio == nil){ + print("usbxhci: cannot map registers\n"); + continue; + } + ctlr = malloc(sizeof(Ctlr)); + if(ctlr == nil){ + print("usbxhci: no memory\n"); + continue; + } + ctlr->base = io; + ctlr->active = nil; + ctlr->pcidev = p; + ctlr->mmio = mmio; + for(i = 0; i < nelem(ctlrs); i++) + if(ctlrs[i] == nil){ + ctlrs[i] = ctlr; + break; + } + if(i >= nelem(ctlrs)) + print("xhci: bug: more than %d controllers\n", nelem(ctlrs)); + } +} + +static int +reset(Hci *hp) +{ + Ctlr *ctlr; + int i; + + if(getconf("*nousbxhci")) + return -1; + + scanpci(); + + /* + * Any adapter matches if no hp->port is supplied, + * otherwise the ports must match. + */ + for(i = 0; i < nelem(ctlrs) && ctlrs[i] != nil; i++){ + ctlr = ctlrs[i]; + if(ctlr->active == nil) + if(hp->port == 0 || hp->port == ctlr->base){ + ctlr->active = hp; + goto Found; + } + } + return -1; + +Found: + hp->aux = ctlr; + hp->port = ctlr->base; + hp->irq = ctlr->pcidev->intl; + hp->tbdf = ctlr->pcidev->tbdf; + + hp->init = init; + hp->dump = dump; + hp->interrupt = interrupt; + hp->epopen = epopen; + hp->epclose = epclose; + hp->epread = epread; + hp->epwrite = epwrite; + hp->seprintep = seprintep; + hp->portenable = portenable; + hp->portreset = portreset; + hp->portstatus = portstatus; + hp->shutdown = shutdown; + hp->debug = setdebug; + hp->type = "xhci"; + + return 0; +} + +void +usbxhcilink(void) +{ + addhcitype("xhci", reset); +} + +void +dmaflush(int clean, void *p, ulong len) +{ + uintptr s = (uintptr)p; + uintptr e = (uintptr)p + len; + + if(clean){ + s &= ~(BLOCKALIGN-1); + e += BLOCKALIGN-1; + e &= ~(BLOCKALIGN-1); + cachedwbse((void*)s, e - s); + return; + } + if(s & BLOCKALIGN-1){ + s &= ~(BLOCKALIGN-1); + cachedwbinvse((void*)s, BLOCKALIGN); + s += BLOCKALIGN; + } + if(e & BLOCKALIGN-1){ + e &= ~(BLOCKALIGN-1); + if(e < s) + return; + cachedwbinvse((void*)e, BLOCKALIGN); + } + if(s < e) + cachedinvse((void*)s, e - s); +} 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 Sun Apr 11 00:00:00 2021 @@ -12,6 +12,7 @@ typedef struct Prophdr Prophdr; typedef struct Fbinfo Fbinfo; +typedef struct Vgpio Vgpio; enum { Read = 0x00>>2, @@ -33,12 +34,18 @@ TagResp = 1<<31, TagGetfwrev = 0x00000001, + TagGetrev = 0x00010002, TagGetmac = 0x00010003, + TagGetser = 0x00010004, TagGetram = 0x00010005, TagGetpower = 0x00020001, TagSetpower = 0x00028001, Powerwait = 1<<1, TagGetclkspd= 0x00030002, + TagGetclkmax= 0x00030004, + TagSetclkspd= 0x00038002, + TagGettemp = 0x00030006, + TagXhciReset= 0x00030058, TagFballoc = 0x00040001, TagFbfree = 0x00048001, TagFbblank = 0x00040002, @@ -48,8 +55,11 @@ TagSetvres = 0x00048004, TagGetdepth = 0x00040005, TagSetdepth = 0x00048005, - TagGetrgb = 0x00044006, + TagGetrgb = 0x00040006, TagSetrgb = 0x00048006, + TagGetGpio = 0x00040010, + + Nvgpio = 2, }; struct Fbinfo { @@ -75,6 +85,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 +133,8 @@ uintptr r; int n; Prophdr *prop; - static uintptr base = BUSDRAM; + uintptr aprop; + static int busaddr = 1; if(rsplen < vallen) rsplen = vallen; @@ -131,13 +151,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 +180,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,10 +210,10 @@ 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); + va = mmukmap(FRAMEBUFFER, fi->base & ~0xC0000000, fi->screensize); if(va) memset((char*)va, 0x7F, fi->screensize); return (void*)va; @@ -251,6 +276,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 @@ -264,6 +302,19 @@ } /* + * Get serial number + */ +uvlong +getserial(void) +{ + uvlong buf; + + if(vcreq(TagGetser, &buf, 0, sizeof buf) != sizeof buf) + return 0; + return buf; +} + +/* * Get ARM ram */ void @@ -289,4 +340,81 @@ 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]; +} + +/* + * Notify gpu that xhci firmware might need loading. This is for some + * pi4 board versions which are missing the eeprom chip for the vl805, + * requiring its firmware to come from the boot eeprom instead. + */ +int +xhcireset(int devaddr) +{ + u32int buf[1]; + + buf[0] = devaddr; + if(vcreq(TagXhciReset, buf, sizeof(buf), sizeof(buf[0])) == sizeof(buf[0])) + return buf[0]; + return -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 Sun Apr 11 00:00:00 2021 @@ -1 +1,512 @@ -#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; + cpwrcpaccess(MASK(28)); + acc = cprdcpaccess(); + 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 = fprdsid(); + 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) { + fpwrexc(0); + m->fpon = 0; + } +} + +void +fpononly(void) +{ + if (!m->fpon && havefp()) { + /* enable fp. must be first operation on the FPUs. */ + fpwrexc(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; + fpwrscr(m->fpscr); + m->fpconfiged = 1; + + if (printed) + return; + sid = fprdsid(); + 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) + fpwrscr((fprdscr() & Allcc) | m->fpscr); + else + fpcfg(); /* 1st time on this fpu; configure it */ + } +} + +void +fpclear(void) +{ +// ulong scr; + + fpon(); +// scr = fprdscr(); +// m->fpscr = scr & ~Allexc; +// fpwrscr(m->fpscr); + + fpwrexc(fprdexc() & ~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) +{ + fpon(); + fps->control = fps->status = fprdscr(); + assert(m->fpnregs); + fpsaveregs((uvlong*)fps->regs, m->fpnregs); + fpoff(); +} + +static void +fprestore(Proc *p) +{ + fpon(); + fpwrscr(p->fpsave.control); + m->fpscr = fprdscr() & ~Allcc; + assert(m->fpnregs); + fprestregs((uvlong*)p->fpsave.regs, m->fpnregs); +} + +/* + * 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 Sun Apr 11 00:00:00 2021 @@ -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 diff -Nru /n/sources/plan9/sys/src/9/bcm/words.pi4 /sys/src/9/bcm/words.pi4 --- /n/sources/plan9/sys/src/9/bcm/words.pi4 Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/words.pi4 Sun Apr 11 00:00:00 2021 @@ -0,0 +1,14 @@ +raspberry pi 4 -- work in progress! + +broadcom 2838 SoC (based on 2711) +4 x cortex-a72 (1500Mhz, out-of-order pipeline) + +Needs firmware from 5 July 2019 or later. + +config.txt for pi4 should include 'core_freq=250' for +the mini-uart console, and 'device_tree=' to ensure that +the loader passes an ATAG list to the kernel instead of +a device tree. + +On a 4GB (or 2GB) pi4, a little less than 2GB will be +used. (0x7D000000 bytes to be precise.)