From e5888a1ffdae813d7575f5fb02275c6bb07e5199 Mon Sep 17 00:00:00 2001 From: Taru Karttunen Date: Wed, 30 Mar 2011 15:46:40 +0300 Subject: Import sources from 2011-03-30 iso image --- sys/src/9/omap/arch.c | 223 +++++++ sys/src/9/omap/archomap.c | 1389 +++++++++++++++++++++++++++++++++++++++ sys/src/9/omap/arm.h | 246 +++++++ sys/src/9/omap/arm.s | 95 +++ sys/src/9/omap/beagle | 82 +++ sys/src/9/omap/cache.v7.s | 208 ++++++ sys/src/9/omap/clock.c | 489 ++++++++++++++ sys/src/9/omap/coproc.c | 133 ++++ sys/src/9/omap/dat.h | 303 +++++++++ sys/src/9/omap/devarch.c | 200 ++++++ sys/src/9/omap/devcons.c | 1354 ++++++++++++++++++++++++++++++++++++++ sys/src/9/omap/devdss.c | 180 ++++++ sys/src/9/omap/devether.c | 530 +++++++++++++++ sys/src/9/omap/devuart.c | 789 +++++++++++++++++++++++ sys/src/9/omap/devusb.c | 1461 ++++++++++++++++++++++++++++++++++++++++++ sys/src/9/omap/dma.c | 263 ++++++++ sys/src/9/omap/ether9221.c | 961 +++++++++++++++++++++++++++ sys/src/9/omap/etherif.h | 41 ++ sys/src/9/omap/fns.h | 178 +++++ sys/src/9/omap/fpi.c | 300 +++++++++ sys/src/9/omap/fpi.h | 61 ++ sys/src/9/omap/fpiarm.c | 576 +++++++++++++++++ sys/src/9/omap/fpimem.c | 136 ++++ sys/src/9/omap/init9.s | 25 + sys/src/9/omap/io.h | 82 +++ sys/src/9/omap/kbd.c | 410 ++++++++++++ sys/src/9/omap/l.s | 569 ++++++++++++++++ sys/src/9/omap/lexception.s | 187 ++++++ sys/src/9/omap/lproc.s | 47 ++ sys/src/9/omap/main.c | 698 ++++++++++++++++++++ sys/src/9/omap/mem.h | 212 ++++++ sys/src/9/omap/mkfile | 136 ++++ sys/src/9/omap/mmu.c | 496 ++++++++++++++ sys/src/9/omap/mouse.c | 255 ++++++++ sys/src/9/omap/notes/movm.w | 22 + sys/src/9/omap/nvram | Bin 0 -> 512 bytes sys/src/9/omap/random.c | 138 ++++ sys/src/9/omap/rebootcode.s | 209 ++++++ sys/src/9/omap/screen.c | 791 +++++++++++++++++++++++ sys/src/9/omap/screen.h | 105 +++ sys/src/9/omap/softfpu.c | 119 ++++ sys/src/9/omap/syscall.c | 333 ++++++++++ sys/src/9/omap/trap.c | 769 ++++++++++++++++++++++ sys/src/9/omap/uarti8250.c | 850 ++++++++++++++++++++++++ sys/src/9/omap/ucalloc.c | 135 ++++ sys/src/9/omap/ucallocb.c | 152 +++++ sys/src/9/omap/uncached.h | 36 ++ sys/src/9/omap/usbehci.h | 243 +++++++ sys/src/9/omap/usbehciomap.c | 224 +++++++ sys/src/9/omap/words | 202 ++++++ 50 files changed, 17643 insertions(+) create mode 100755 sys/src/9/omap/arch.c create mode 100755 sys/src/9/omap/archomap.c create mode 100755 sys/src/9/omap/arm.h create mode 100755 sys/src/9/omap/arm.s create mode 100755 sys/src/9/omap/beagle create mode 100755 sys/src/9/omap/cache.v7.s create mode 100755 sys/src/9/omap/clock.c create mode 100755 sys/src/9/omap/coproc.c create mode 100755 sys/src/9/omap/dat.h create mode 100755 sys/src/9/omap/devarch.c create mode 100755 sys/src/9/omap/devcons.c create mode 100755 sys/src/9/omap/devdss.c create mode 100755 sys/src/9/omap/devether.c create mode 100755 sys/src/9/omap/devuart.c create mode 100755 sys/src/9/omap/devusb.c create mode 100755 sys/src/9/omap/dma.c create mode 100755 sys/src/9/omap/ether9221.c create mode 100755 sys/src/9/omap/etherif.h create mode 100755 sys/src/9/omap/fns.h create mode 100755 sys/src/9/omap/fpi.c create mode 100755 sys/src/9/omap/fpi.h create mode 100755 sys/src/9/omap/fpiarm.c create mode 100755 sys/src/9/omap/fpimem.c create mode 100755 sys/src/9/omap/init9.s create mode 100755 sys/src/9/omap/io.h create mode 100755 sys/src/9/omap/kbd.c create mode 100755 sys/src/9/omap/l.s create mode 100755 sys/src/9/omap/lexception.s create mode 100755 sys/src/9/omap/lproc.s create mode 100755 sys/src/9/omap/main.c create mode 100755 sys/src/9/omap/mem.h create mode 100755 sys/src/9/omap/mkfile create mode 100755 sys/src/9/omap/mmu.c create mode 100755 sys/src/9/omap/mouse.c create mode 100755 sys/src/9/omap/notes/movm.w create mode 100755 sys/src/9/omap/nvram create mode 100755 sys/src/9/omap/random.c create mode 100755 sys/src/9/omap/rebootcode.s create mode 100755 sys/src/9/omap/screen.c create mode 100755 sys/src/9/omap/screen.h create mode 100755 sys/src/9/omap/softfpu.c create mode 100755 sys/src/9/omap/syscall.c create mode 100755 sys/src/9/omap/trap.c create mode 100755 sys/src/9/omap/uarti8250.c create mode 100755 sys/src/9/omap/ucalloc.c create mode 100755 sys/src/9/omap/ucallocb.c create mode 100755 sys/src/9/omap/uncached.h create mode 100755 sys/src/9/omap/usbehci.h create mode 100755 sys/src/9/omap/usbehciomap.c create mode 100755 sys/src/9/omap/words (limited to 'sys/src/9/omap') diff --git a/sys/src/9/omap/arch.c b/sys/src/9/omap/arch.c new file mode 100755 index 000000000..a9b9537a9 --- /dev/null +++ b/sys/src/9/omap/arch.c @@ -0,0 +1,223 @@ +#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 sysfile.c + */ +void +evenaddr(uintptr addr) +{ + if(addr & 3){ + postnote(up, 1, "sys: odd address", NDebug); + error(Ebadarg); + } +} + +/* 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); +} + +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; +} + +/* + * atomic ops + * make sure that we don't drag in the C library versions + */ + +long +_xdec(long *p) +{ + int s, v; + + s = splhi(); + v = --*p; + splx(s); + return v; +} + +void +_xinc(long *p) +{ + int s; + + s = splhi(); + ++*p; + splx(s); +} + +int +ainc(int *p) +{ + int s, v; + + s = splhi(); + v = ++*p; + splx(s); + return v; +} + +int +adec(int *p) +{ + int s, v; + + s = splhi(); + v = --*p; + splx(s); + return v; +} + +int +cas32(void* addr, u32int old, u32int new) +{ + int r, s; + + s = splhi(); + if(r = (*(u32int*)addr == old)) + *(u32int*)addr = new; + splx(s); + if (r) + coherence(); + return r; +} diff --git a/sys/src/9/omap/archomap.c b/sys/src/9/omap/archomap.c new file mode 100755 index 000000000..0f20d50a6 --- /dev/null +++ b/sys/src/9/omap/archomap.c @@ -0,0 +1,1389 @@ +/* + * omap3530 SoC (e.g. beagleboard) architecture-specific stuff + * + * errata: usb port 3 cannot operate in ulpi mode, only serial or + * ulpi tll mode + */ + +#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" +#include "../port/flashif.h" +#include "../port/usb.h" +#include "usbehci.h" + +#define FREQSEL(x) ((x) << 4) + +typedef struct Cm Cm; +typedef struct Cntrl Cntrl; +typedef struct Gen Gen; +typedef struct Gpio Gpio; +typedef struct L3agent L3agent; +typedef struct L3protreg L3protreg; +typedef struct L3regs L3regs; +typedef struct Prm Prm; +typedef struct Usbotg Usbotg; +typedef struct Usbtll Usbtll; + +/* omap3 non-standard usb stuff */ +struct Usbotg { + uchar faddr; + uchar power; + ushort intrtx; + ushort intrrx; + ushort intrtxe; + ushort intrrxe; + uchar intrusb; + uchar intrusbe; + ushort frame; + uchar index; + uchar testmode; + + /* indexed registers follow; ignore for now */ + uchar _pad0[0x400 - 0x10]; + + ulong otgrev; + ulong otgsyscfg; + ulong otgsyssts; + ulong otgifcsel; /* interface selection */ + uchar _pad1[0x414 - 0x410]; + ulong otgforcestdby; +}; + +enum { + /* power bits */ + Hsen = 1<<5, /* high-speed enable */ + + /* testmode bits */ + Forcehost = 1<<7, /* force host (vs peripheral) mode */ + Forcehs = 1<<4, /* force high-speed at reset */ + + /* otgsyscfg bits */ + Midle = 1<<12, /* no standby mode */ + Sidle = 1<<3, /* no idle mode */ +// Softreset = 1<<1, + + /* otgsyssts bits, per sysstatus */ +}; + +struct Usbtll { + ulong revision; /* ro */ + uchar _pad0[0x10-0x4]; + ulong sysconfig; + ulong sysstatus; /* ro */ + + ulong irqstatus; + ulong irqenable; +}; + +enum { + /* sysconfig bits */ + Softreset = 1<<1, + + /* sysstatus bits */ + Resetdone = 1<<0, + /* only in uhh->sysstatus */ + Ehci_resetdone = 1<<2, + Ohci_resetdone = 1<<1, +}; + +/* + * an array of these structs is preceded by error_log at 0x20, control, + * error_clear_single, error_clear_multi. first struct is at offset 0x48. + */ +struct L3protreg { /* hw: an L3 protection region */ + uvlong req_info_perm; + uvlong read_perm; + uvlong write_perm; + uvlong addr_match; /* ro? write this one last, then flush */ +}; + +// TODO: set these permission bits (e.g., for usb)? +enum { + Permusbhost = 1<<9, + Permusbotg = 1<<4, + Permsysdma = 1<<3, + Permmpu = 1<<1, +}; + +struct L3agent { /* hw registers */ + uchar _pad0[0x20]; + uvlong ctl; + uvlong sts; + uchar _pad1[0x58 - 0x30]; + uvlong errlog; + uvlong errlogaddr; +}; + +struct L3regs { + L3protreg *base; /* base of array */ + int upper; /* index maximum */ + char *name; +}; +L3regs l3regs[] = { + (L3protreg *)(PHYSL3GPMCPM+0x48), 7, "gpmc", /* known to be first */ + (L3protreg *)(PHYSL3PMRT+0x48), 1, "rt", /* l3 config */ + (L3protreg *)(PHYSL3OCTRAM+0x48), 7, "ocm ram", + (L3protreg *)(PHYSL3OCTROM+0x48), 1, "ocm rom", + (L3protreg *)(PHYSL3MAD2D+0x48), 7, "mad2d", /* die-to-die */ + (L3protreg *)(PHYSL3IVA+0x48), 3, "iva2.2", /* a/v */ +}; + +/* + * PRM_CLKSEL (0x48306d40) low 3 bits are system clock speed, assuming + * units of MHz: 0 = 12, 1 = 13, 2 = 19.2, 3 = 26, 4 = 38.4, 5 = 16.8 + */ + +struct Cm { /* clock management */ + ulong fclken; /* ``functional'' clock enable */ + ulong fclken2; + ulong fclken3; + uchar _pad0[0x10 - 0xc]; + + ulong iclken; /* ``interface'' clock enable */ + ulong iclken2; + ulong iclken3; + uchar _pad1[0x20 - 0x1c]; + + ulong idlest; /* idle status */ + ulong idlest2; + ulong idlest3; + uchar _pad2[0x30 - 0x2c]; + + ulong autoidle; + ulong autoidle2; + ulong autoidle3; + uchar _pad3[0x40 - 0x3c]; + + union { + ulong clksel[5]; + struct unused { + ulong sleepdep; + ulong clkstctrl; + ulong clkstst; + }; + uchar _pad4[0x70 - 0x40]; + }; + ulong clkoutctrl; +}; + +struct Prm { /* power & reset management */ + uchar _pad[0x50]; + ulong rstctrl; +}; + +struct Gpio { + ulong _pad0[4]; + ulong sysconfig; + ulong sysstatus; + + ulong irqsts1; /* for mpu */ + ulong irqen1; + ulong wkupen; + ulong _pad1; + ulong irqsts2; /* for iva */ + ulong irqen2; + + ulong ctrl; + + ulong oe; + ulong datain; + ulong dataout; + + ulong lvldet0; + ulong lvldet1; + ulong risingdet; + ulong fallingdet; + + /* rest are uninteresting */ + ulong deben; /* debouncing enable */ + ulong debtime; + ulong _pad2[2]; + + ulong clrirqen1; + ulong setirqen1; + ulong _pad3[2]; + + ulong clrirqen2; + ulong setirqen2; + ulong _pad4[2]; + + ulong clrwkupen; + ulong setwkupen; + ulong _pad5[2]; + + ulong clrdataout; + ulong setdataout; +}; + +enum { + /* clock enable & idle status bits */ + Wkusimocp = 1 << 9, /* SIM card: uses 120MHz clock */ + Wkwdt2 = 1 << 5, /* wdt2 clock enable bit for wakeup */ + Wkgpio1 = 1 << 3, /* gpio1 " */ + Wkgpt1 = 1 << 0, /* gpt1 " */ + + Dssl3l4 = 1 << 0, /* dss l3, l4 i clks */ + Dsstv = 1 << 2, /* dss tv f clock */ + Dss2 = 1 << 1, /* dss clock 2 */ + Dss1 = 1 << 0, /* dss clock 1 */ + + Pergpio6 = 1 << 17, + Pergpio5 = 1 << 16, + Pergpio4 = 1 << 15, + Pergpio3 = 1 << 14, + Pergpio2 = 1 << 13, + Perwdt3 = 1 << 12, /* wdt3 clock enable bit for periphs */ + Peruart3 = 1 << 11, /* console uart */ + Pergpt9 = 1 << 10, + Pergpt8 = 1 << 9, + Pergpt7 = 1 << 8, + Pergpt6 = 1 << 7, + Pergpt5 = 1 << 6, + Pergpt4 = 1 << 5, + Pergpt3 = 1 << 4, + Pergpt2 = 1 << 3, /* gpt2 clock enable bit for periphs */ + + Perenable = Pergpio6 | Pergpio5 | Perwdt3 | Pergpt2 | Peruart3, + + Usbhost2 = 1 << 1, /* 120MHz clock enable */ + Usbhost1 = 1 << 0, /* 48MHz clock enable */ + Usbhost = Usbhost1, /* iclock enable */ + Usbhostidle = 1 << 1, + Usbhoststdby = 1 << 0, + + Coreusbhsotg = 1 << 4, /* usb hs otg enable bit */ + Core3usbtll = 1 << 2, /* usb tll enable bit */ + + /* core->idlest bits */ + Coreusbhsotgidle = 1 << 5, + Coreusbhsotgstdby= 1 << 4, + + Dplllock = 7, + + /* mpu->idlest2 bits */ + Dplllocked = 1, + Dpllbypassed = 0, + + /* wkup->idlest bits */ + Gpio1idle = 1 << 3, + + /* dss->idlest bits */ + Dssidle = 1 << 1, + + Gpio1vidmagic = 1<<24 | 1<<8 | 1<<5, /* gpio 1 pins for video */ +}; +enum { + Rstgs = 1 << 1, /* global sw. reset */ + + /* fp control regs. most are read-only */ + Fpsid = 0, + Fpscr, /* rw */ + Mvfr1 = 6, + Mvfr0, + Fpexc, /* rw */ +}; + +/* see ether9221.c for explanation */ +enum { + Ethergpio = 176, + Etherchanbit = 1 << (Ethergpio % 32), +}; + +/* + * 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 + */ +enum { + /* + * cache capabilities. write-back vs write-through is controlled + * by the Buffered bit in PTEs. + */ + Cawt = 1 << 31, + Cawb = 1 << 30, + Cara = 1 << 29, + Cawa = 1 << 28, +}; + +struct Gen { + ulong padconf_off; + ulong devconf0; + uchar _pad0[0x68 - 8]; + ulong devconf1; +}; + +struct Cntrl { + ulong _pad0; + ulong id; + ulong _pad1; + ulong skuid; +}; + + +static char * +devidstr(ulong) +{ + return "ARM Cortex-A8"; +} + +void +archomaplink(void) +{ +} + +int +ispow2(uvlong ul) +{ + /* see Hacker's Delight if this isn't obvious */ + return (ul & (ul - 1)) == 0; +} + +/* + * return exponent of smallest power of 2 ≥ n + */ +int +log2(ulong n) +{ + int i; + + i = 31 - clz(n); + if (n == 0 || !ispow2(n)) + i++; + return i; +} + +void +archconfinit(void) +{ + char *p; + ulong mhz; + + assert(m != nil); + m->cpuhz = 500 * Mhz; /* beagle speed */ + p = getconf("*cpumhz"); + if (p) { + mhz = atoi(p) * Mhz; + if (mhz >= 100*Mhz && mhz <= 3000UL*Mhz) + m->cpuhz = mhz; + } + m->delayloop = m->cpuhz/2000; /* initial estimate */ +} + +static void +prperm(uvlong perm) +{ + if (perm == MASK(16)) + print("all"); + else + print("%#llux", perm); +} + +static void +prl3region(L3protreg *pr, int r) +{ + int level, size, addrspace; + uvlong am, base; + + if (r == 0) + am = 0; + else + am = pr->addr_match; + size = (am >> 3) & MASK(5); + if (r > 0 && size == 0) /* disabled? */ + return; + + print(" %d: perms req ", r); + prperm(pr->req_info_perm); + if (pr->read_perm == pr->write_perm && pr->read_perm == MASK(16)) + print(" rw all"); + else { + print(" read "); + prperm(pr->read_perm); + print(" write "); + prperm(pr->write_perm); + } + if (r == 0) + print(", all addrs level 0"); + else { + size = 1 << size; /* 2^size */ + level = (am >> 9) & 1; + if (r == 1) + level = 3; + else + level++; + addrspace = am & 7; + base = am & ~MASK(10); + print(", base %#llux size %dKB level %d addrspace %d", + base, size, level, addrspace); + } + print("\n"); + delay(100); +} + + +/* + * dump the l3 interconnect firewall settings by protection region. + * mpu, sys dma and both usbs (0x21a) should be set in all read & write + * permission registers. + */ +static void +dumpl3pr(void) +{ + int r; + L3regs *reg; + L3protreg *pr; + + for (reg = l3regs; reg < l3regs + nelem(l3regs); reg++) { + print("%#p (%s) enabled l3 regions:\n", reg->base, reg->name); + for (r = 0; r <= reg->upper; r++) + prl3region(reg->base + r, r); + } +if (0) { // TODO + /* touch up gpmc perms */ + reg = l3regs; /* first entry is gpmc */ + for (r = 0; r <= reg->upper; r++) { + pr = reg->base + r; + // TODO + } + print("%#p (%s) modified l3 regions:\n", reg->base, reg->name); + for (r = 0; r <= reg->upper; r++) + prl3region(reg->base + r, r); +} +} + +static void +p16(uchar *p, ulong v) +{ + *p++ = v>>8; + *p = v; +} + +static void +p32(uchar *p, ulong v) +{ + *p++ = v>>24; + *p++ = v>>16; + *p++ = v>>8; + *p = v; +} + +int +archether(unsigned ctlrno, Ether *ether) +{ + switch(ctlrno) { + case 0: + /* there's no built-in ether on the beagle but igepv2 has 1 */ + ether->type = "9221"; + ether->ctlrno = ctlrno; + ether->irq = 34; + ether->nopt = 0; + ether->mbps = 100; + return 1; + } + return -1; +} + +/* + * turn on all the necessary clocks on the SoC. + * + * a ``functional'' clock drives a device; an ``interface'' clock drives + * its communication with the rest of the system. so the interface + * clock must be enabled to reach the device's registers. + * + * dplls: 1 mpu, 2 iva2, 3 core, 4 per, 5 per2. + */ + +static void +configmpu(void) +{ + ulong clk, mhz, nmhz, maxmhz; + Cm *mpu = (Cm *)PHYSSCMMPU; + Cntrl *id = (Cntrl *)PHYSCNTRL; + + if ((id->skuid & MASK(4)) == 8) + maxmhz = 720; + else + maxmhz = 600; + iprint("cpu capable of %ldMHz operation", maxmhz); + + clk = mpu->clksel[0]; + mhz = (clk >> 8) & MASK(11); /* configured speed */ +// iprint("\tfclk src %ld; dpll1 mult %ld (MHz) div %ld", +// (clk >> 19) & MASK(3), mhz, clk & MASK(7)); + iprint("; at %ldMHz", mhz); + nmhz = m->cpuhz / Mhz; /* nominal speed */ + if (mhz == nmhz) { + iprint("\n"); + return; + } + + mhz = nmhz; + if (mhz > maxmhz) { + mhz = maxmhz; + iprint("; limiting operation to %ldMHz", mhz); + } + + /* disable dpll1 lock mode; put into low-power bypass mode */ + mpu->fclken2 = mpu->fclken2 & ~MASK(3) | 5; + coherence(); + while (mpu->idlest2 != Dpllbypassed) + ; + + /* + * there's a dance to change processor speed, + * prescribed in spruf98d §4.7.6.9. + */ + + /* just change multiplier; leave divider alone at 12 (meaning 13?) */ + mpu->clksel[0] = clk & ~(MASK(11) << 8) | mhz << 8; + coherence(); + + /* set output divider (M2) in clksel[1]: leave at 1 */ + + /* + * u-boot calls us with just freqsel 3 (~1MHz) & dpll1 lock mode. + */ + /* set FREQSEL */ + mpu->fclken2 = mpu->fclken2 & ~FREQSEL(MASK(4)) | FREQSEL(3); + coherence(); + + /* set ramp-up delay to `fast' */ + mpu->fclken2 = mpu->fclken2 & ~(MASK(2) << 8) | 3 << 8; + coherence(); + + /* set auto-recalibration (off) */ + mpu->fclken2 &= ~(1 << 3); + coherence(); + + /* disable auto-idle: ? */ + /* unmask clock intr: later */ + + /* enable dpll lock mode */ + mpu->fclken2 |= Dplllock; + coherence(); + while (mpu->idlest2 != Dplllocked) + ; + delay(200); /* allow time for speed to ramp up */ + + if (((mpu->clksel[0] >> 8) & MASK(11)) != mhz) + panic("mpu clock speed change didn't stick"); + iprint("; now at %ldMHz\n", mhz); +} + +static void +configpll(void) +{ + int i; + Cm *pll = (Cm *)PHYSSCMPLL; + + pll->clkoutctrl |= 1 << 7; /* enable sys_clkout2 */ + coherence(); + delay(10); + + /* + * u-boot calls us with just freqsel 3 (~1MHz) & lock mode + * for both dplls (3 & 4). ensure that. + */ + if ((pll->idlest & 3) != 3) { + /* put dpll[34] into low-power bypass mode */ + pll->fclken = pll->fclken & ~(MASK(3) << 16 | MASK(3)) | + 1 << 16 | 5; + coherence(); + while (pll->idlest & 3) /* wait for both to bypass or stop */ + ; + + pll->fclken = (FREQSEL(3) | Dplllock) << 16 | + FREQSEL(3) | Dplllock; + coherence(); + while ((pll->idlest & 3) != 3) /* wait for both to lock */ + ; + } + + /* + * u-boot calls us with just freqsel 1 (default but undefined) + * & stop mode for dpll5. try to lock it at 120MHz. + */ + if (!(pll->idlest2 & Dplllocked)) { + /* force dpll5 into low-power bypass mode */ + pll->fclken2 = 3 << 8 | FREQSEL(1) | 1; + coherence(); + for (i = 0; pll->idlest2 & Dplllocked && i < 20; i++) + delay(50); + if (i >= 20) + iprint(" [dpll5 failed to stop]"); + + /* + * CORE_CLK is 26MHz. + */ + pll->clksel[4-1] = 120 << 8 | 12; /* M=120, N=12+1 */ + /* M2 divisor: 120MHz clock is exactly the DPLL5 clock */ + pll->clksel[5-1] = 1; + coherence(); + + pll->fclken2 = 3 << 8 | FREQSEL(1) | Dplllock; /* def. freq */ + coherence(); + + for (i = 0; !(pll->idlest2 & Dplllocked) && i < 20; i++) + delay(50); + if (i >= 20) + iprint(" [dpll5 failed to lock]"); + } + if (!(pll->idlest2 & (1<<1))) + iprint(" [no 120MHz clock]"); + if (!(pll->idlest2 & (1<<3))) + iprint(" [no dpll5 120MHz clock output]"); +} + +static void +configper(void) +{ + Cm *per = (Cm *)PHYSSCMPER; + + per->clksel[0] &= ~MASK(8); /* select 32kHz clock for GPTIMER2-9 */ + + per->iclken |= Perenable; + coherence(); + per->fclken |= Perenable; + coherence(); + while (per->idlest & Perenable) + ; + + per->autoidle = 0; + coherence(); +} + +static void +configwkup(void) +{ + Cm *wkup = (Cm *)PHYSSCMWKUP; + + /* select 32kHz clock (not system clock) for GPTIMER1 */ + wkup->clksel[0] &= ~1; + + wkup->iclken |= Wkusimocp | Wkwdt2 | Wkgpt1; + coherence(); + wkup->fclken |= Wkusimocp | Wkwdt2 | Wkgpt1; + coherence(); + while (wkup->idlest & (Wkusimocp | Wkwdt2 | Wkgpt1)) + ; +} + +static void +configusb(void) +{ + int i; + Cm *usb = (Cm *)PHYSSCMUSB; + + /* + * make the usb registers accessible without address faults, + * notably uhh, ochi & ehci. tll seems to be separate & otg is okay. + */ + usb->iclken |= Usbhost; + coherence(); + usb->fclken |= Usbhost1 | Usbhost2; /* includes 120MHz clock */ + coherence(); + for (i = 0; usb->idlest & Usbhostidle && i < 20; i++) + delay(50); + if (i >= 20) + iprint(" [usb inaccessible]"); +} + +static void +configcore(void) +{ + Cm *core = (Cm *)PHYSSCMCORE; + + /* + * make the usb tll registers accessible. + */ + core->iclken |= Coreusbhsotg; + core->iclken3 |= Core3usbtll; + coherence(); + core->fclken3 |= Core3usbtll; + coherence(); + delay(100); + while (core->idlest & Coreusbhsotgidle) + ; + if (core->idlest3 & Core3usbtll) + iprint(" [no usb tll]"); +} + +static void +configclks(void) +{ + int s; + Gen *gen = (Gen *)PHYSSCMPCONF; + + delay(20); + s = splhi(); + configmpu(); /* sets cpu clock rate, turns on dplls 1 & 2 */ + + /* + * the main goal is to get enough clocks running, in the right order, + * so that usb has all the necessary clock signals. + */ + iprint("clocks:"); + iprint(" usb"); + configusb(); /* starts usb clocks & 120MHz clock */ + iprint(", pll"); + configpll(); /* starts dplls 3, 4 & 5 & 120MHz clock */ + iprint(", wakeup"); + configwkup(); /* starts timer clocks and usim clock */ + iprint(", per"); + configper(); /* starts timer & gpio (ether) clocks */ + iprint(", core"); + configcore(); /* starts usb tll */ + iprint("\n"); + + gen->devconf0 |= 1 << 1 | 1 << 0; /* dmareq[01] edge sensitive */ + /* make dmareq[2-6] edge sensitive */ + gen->devconf1 |= 1 << 23 | 1 << 22 | 1 << 21 | 1 << 8 | 1 << 7; + coherence(); + splx(s); + delay(20); +} + +static void +resetwait(ulong *reg) +{ + long bound; + + for (bound = 400*Mhz; !(*reg & Resetdone) && bound > 0; bound--) + ; + if (bound <= 0) + iprint("archomap: Resetdone didn't come ready\n"); +} + +/* + * gpio irq 1 goes to the mpu intr ctlr; irq 2 goes to the iva's. + * this stuff is magic and without it, we won't get irq 34 interrupts + * from the 9221 ethernet controller. + */ +static void +configgpio(void) +{ + Gpio *gpio = (Gpio *)PHYSGPIO6; + + gpio->sysconfig = Softreset; + coherence(); + resetwait(&gpio->sysstatus); + + gpio->ctrl = 1<<1 | 0; /* enable this gpio module, gating ratio 1 */ + gpio->oe |= Etherchanbit; /* cfg ether pin as input */ + coherence(); + + gpio->irqen1 = Etherchanbit; /* channel # == pin # */ + gpio->irqen2 = 0; + + gpio->lvldet0 = Etherchanbit; /* enable irq ass'n on low det'n */ + gpio->lvldet1 = 0; /* disable irq ass'n on high det'n */ + gpio->risingdet = 0; /* enable irq rising edge det'n */ + gpio->fallingdet = 0; /* disable irq falling edge det'n */ + + gpio->wkupen = 0; + + gpio->deben = 0; /* no de-bouncing */ + gpio->debtime = 0; + coherence(); + + gpio->irqsts1 = ~0; /* dismiss all outstanding intrs */ + gpio->irqsts2 = ~0; + coherence(); +} + +void +configscreengpio(void) +{ + Cm *wkup = (Cm *)PHYSSCMWKUP; + Gpio *gpio = (Gpio *)PHYSGPIO1; + + /* no clocksel needed */ + wkup->iclken |= Wkgpio1; + coherence(); + wkup->fclken |= Wkgpio1; /* turn gpio clock on */ + coherence(); + // wkup->autoidle |= Wkgpio1; /* set gpio clock on auto */ + wkup->autoidle = 0; + coherence(); + while (wkup->idlest & Gpio1idle) + ; + + /* + * 0 bits in oe are output signals. + * enable output for gpio 1 (first gpio) video magic pins. + */ + gpio->oe &= ~Gpio1vidmagic; + coherence(); + gpio->dataout |= Gpio1vidmagic; /* set output pins to 1 */ + coherence(); + delay(50); +} + +void +screenclockson(void) +{ + Cm *dss = (Cm *)PHYSSCMDSS; + + dss->iclken |= Dssl3l4; + coherence(); + dss->fclken = Dsstv | Dss2 | Dss1; + coherence(); + /* tv fclk is dpll4 clk; dpll4 m4 divide factor for dss1 fclk is 2 */ + dss->clksel[0] = 1<<12 | 2; + coherence(); + delay(50); + while (dss->idlest & Dssidle) + ; +} + +void +gpioirqclr(void) +{ + Gpio *gpio = (Gpio *)PHYSGPIO6; + + gpio->irqsts1 = gpio->irqsts1; + coherence(); +} + +static char * +l1iptype(uint type) +{ + static char *types[] = { + "reserved", + "asid-tagged VIVT", + "VIPT", + "PIPT", + }; + + if (type >= nelem(types) || types[type] == nil) + return "GOK"; + return types[type]; +} + +void +cacheinfo(int level, Memcache *cp) +{ + ulong setsways; + + /* select cache level */ + cpwrsc(CpIDcssel, CpID, CpIDid, 0, (level - 1) << 1); + + setsways = cprdsc(CpIDcsize, CpID, CpIDid, 0); + cp->l1ip = cprdsc(0, CpID, CpIDidct, CpIDct); + cp->level = level; + cp->nways = ((setsways >> 3) & MASK(10)) + 1; + cp->nsets = ((setsways >> 13) & MASK(15)) + 1; + cp->log2linelen = (setsways & MASK(2)) + 2 + 2; + cp->linelen = 1 << cp->log2linelen; + cp->setsways = setsways; + + cp->setsh = cp->log2linelen; + cp->waysh = 32 - log2(cp->nways); +} + +static void +prcachecfg(void) +{ + int cache; + Memcache mc; + + for (cache = 1; cache <= 2; cache++) { + cacheinfo(cache, &mc); + iprint("l%d: %d ways %d sets %d bytes/line", + mc.level, mc.nways, mc.nsets, mc.linelen); + if (mc.linelen != CACHELINESZ) + iprint(" *should* be %d", CACHELINESZ); + if (mc.setsways & Cawt) + iprint("; can WT"); + if (mc.setsways & Cawb) + iprint("; can WB"); +#ifdef COMPULSIVE /* both caches can do this */ + if (mc.setsways & Cara) + iprint("; can read-allocate"); +#endif + if (mc.setsways & Cawa) + iprint("; can write-allocate"); + if (cache == 1) + iprint("; l1 I policy %s", + l1iptype((mc.l1ip >> 14) & MASK(2))); + iprint("\n"); + } +} + +static char * +subarch(int impl, uint sa) +{ + static char *armarchs[] = { + "VFPv1 (pre-armv7)", + "VFPv2 (pre-armv7)", + "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]; +} + +/* + * padconf bits in a short, 2 per long register + * 15 wakeupevent + * 14 wakeupenable + * 13 offpulltypeselect + * 12 offpulludenable + * 11 offoutvalue + * 10 offoutenable + * 9 offenable + * 8 inputenable + * 4 pulltypeselect + * 3 pulludenable + * 2-0 muxmode + * + * see table 7-5 in §7.4.4.3 of spruf98d + */ + +enum { + /* pad config register bits */ + Inena = 1 << 8, /* input enable */ + Indis = 0 << 8, /* input disable */ + Ptup = 1 << 4, /* pull type up */ + Ptdown = 0 << 4, /* pull type down */ + Ptena = 1 << 3, /* pull type selection is active */ + Ptdis = 0 << 3, /* pull type selection is inactive */ + Muxmode = MASK(3), + + /* pad config registers relevant to flash */ + GpmcA1 = 0x4800207A, + GpmcA2 = 0x4800207C, + GpmcA3 = 0x4800207E, + GpmcA4 = 0x48002080, + GpmcA5 = 0x48002082, + GpmcA6 = 0x48002084, + GpmcA7 = 0x48002086, + GpmcA8 = 0x48002088, + GpmcA9 = 0x4800208A, + GpmcA10 = 0x4800208C, + GpmcD0 = 0x4800208E, + GpmcD1 = 0x48002090, + GpmcD2 = 0x48002092, + GpmcD3 = 0x48002094, + GpmcD4 = 0x48002096, + GpmcD5 = 0x48002098, + GpmcD6 = 0x4800209A, + GpmcD7 = 0x4800209C, + GpmcD8 = 0x4800209E, + GpmcD9 = 0x480020A0, + GpmcD10 = 0x480020A2, + GpmcD11 = 0x480020A4, + GpmcD12 = 0x480020A6, + GpmcD13 = 0x480020A8, + GpmcD14 = 0x480020AA, + GpmcD15 = 0x480020AC, + GpmcNCS0 = 0x480020AE, + GpmcNCS1 = 0x480020B0, + GpmcNCS2 = 0x480020B2, + GpmcNCS3 = 0x480020B4, + GpmcNCS4 = 0x480020B6, + GpmcNCS5 = 0x480020B8, + GpmcNCS6 = 0x480020BA, + GpmcNCS7 = 0x480020BC, + GpmcCLK = 0x480020BE, + GpmcNADV_ALE = 0x480020C0, + GpmcNOE = 0x480020C2, + GpmcNWE = 0x480020C4, + GpmcNBE0_CLE = 0x480020C6, + GpmcNBE1 = 0x480020C8, + GpmcNWP = 0x480020CA, + GpmcWAIT0 = 0x480020CC, + GpmcWAIT1 = 0x480020CE, + GpmcWAIT2 = 0x480020D0, + GpmcWAIT3 = 0x480020D2, +}; + +/* set SCM pad config mux mode */ +void +setmuxmode(ulong addr, int shorts, int mode) +{ + int omode; + ushort *ptr; + + mode &= Muxmode; + for (ptr = (ushort *)addr; shorts-- > 0; ptr++) { + omode = *ptr & Muxmode; + if (omode != mode) + *ptr = *ptr & ~Muxmode | mode; + } + coherence(); +} + +static void +setpadmodes(void) +{ + int off; + + /* set scm pad modes for usb; hasn't made any difference yet */ + setmuxmode(0x48002166, 7, 5); /* hsusb3_tll* in mode 5; is mode 4 */ + setmuxmode(0x48002180, 1, 5); /* hsusb3_tll_clk; is mode 4 */ + setmuxmode(0x48002184, 4, 5); /* hsusb3_tll_data?; is mode 1 */ + setmuxmode(0x480021a2, 12, 0); /* hsusb0 (console) in mode 0 */ + setmuxmode(0x480021d4, 6, 2); /* hsusb2_tll* (ehci port 2) in mode 2 */ + /* mode 3 is hsusb2_data* */ + setmuxmode(0x480025d8, 18, 6); /* hsusb[12]_tll*; mode 3 is */ + /* hsusb1_data*, hsusb2* */ + + setmuxmode(0x480020e4, 2, 5); /* uart3_rx_* in mode 5 */ + setmuxmode(0x4800219a, 4, 0); /* uart3_* in mode 0 */ + /* uart3_* in mode 2; TODO: conflicts with hsusb0 */ + setmuxmode(0x480021aa, 4, 2); + setmuxmode(0x48002240, 2, 3); /* uart3_* in mode 3 */ + + /* + * igep/gumstix only: mode 4 of 21d2 is gpio_176 (smsc9221 ether irq). + * see ether9221.c for more. + */ + *(ushort *)0x480021d2 = Inena | Ptup | Ptena | 4; + + /* magic from u-boot for flash */ + *(ushort *)GpmcA1 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcA2 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcA3 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcA4 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcA5 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcA6 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcA7 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcA8 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcA9 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcA10 = Indis | Ptup | Ptena | 0; + + *(ushort *)GpmcD0 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD1 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD2 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD3 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD4 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD5 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD6 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD7 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD8 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD9 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD10 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD11 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD12 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD13 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD14 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcD15 = Inena | Ptup | Ptena | 0; + + *(ushort *)GpmcNCS0 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcNCS1 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcNCS2 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcNCS3 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcNCS4 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcNCS5 = Indis | Ptup | Ptena | 0; + *(ushort *)GpmcNCS6 = Indis | Ptup | Ptena | 0; + + *(ushort *)GpmcNOE = Indis | Ptdown | Ptdis | 0; + *(ushort *)GpmcNWE = Indis | Ptdown | Ptdis | 0; + + *(ushort *)GpmcWAIT2 = Inena | Ptup | Ptena | 4; /* GPIO_64 -ETH_NRESET */ + *(ushort *)GpmcNCS7 = Inena | Ptup | Ptena | 1; /* SYS_nDMA_REQ3 */ + + *(ushort *)GpmcCLK = Indis | Ptdown | Ptdis | 0; + + *(ushort *)GpmcNBE1 = Inena | Ptdown | Ptdis | 0; + + *(ushort *)GpmcNADV_ALE = Indis | Ptdown | Ptdis | 0; + *(ushort *)GpmcNBE0_CLE = Indis | Ptdown | Ptdis | 0; + + *(ushort *)GpmcNWP = Inena | Ptdown | Ptdis | 0; + + *(ushort *)GpmcWAIT0 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcWAIT1 = Inena | Ptup | Ptena | 0; + *(ushort *)GpmcWAIT3 = Inena | Ptup | Ptena | 0; + + /* + * magic from u-boot: set 0xe00 bits in gpmc_(nwe|noe|nadv_ale) + * to enable `off' mode for each. + */ + for (off = 0xc0; off <= 0xc4; off += sizeof(short)) + *((ushort *)(PHYSSCM + off)) |= 0xe00; + coherence(); +} + +static char * +implement(uchar impl) +{ + if (impl == 'A') + return "arm"; + else + return "unknown"; +} + +static void +fpon(void) +{ + int gotfp, impl; + ulong acc, scr; + + gotfp = 1 << CpFP | 1 << CpDFP; + cpwrsc(0, CpCONTROL, 0, CpCPaccess, MASK(28)); + acc = cprdsc(0, CpCONTROL, 0, CpCPaccess); + if ((acc & (MASK(2) << (2*CpFP))) == 0) { + gotfp &= ~(1 << CpFP); + print("fpon: no single FP coprocessor\n"); + } + if ((acc & (MASK(2) << (2*CpDFP))) == 0) { + gotfp &= ~(1 << CpDFP); + print("fpon: no double FP coprocessor\n"); + } + if (!gotfp) { + print("fpon: no FP coprocessors\n"); + return; + } + + /* enable fp. must be first operation on the FPUs. */ + fpwr(Fpexc, fprd(Fpexc) | 1 << 30); + + scr = fprd(Fpsid); + impl = scr >> 24; + print("fp: %s arch %s", implement(impl), + subarch(impl, (scr >> 16) & MASK(7))); + + scr = fprd(Fpscr); + // TODO configure Fpscr further + scr |= 1 << 9; /* div-by-0 exception */ + scr &= ~(MASK(2) << 20 | MASK(3) << 16); /* all ops are scalar */ + fpwr(Fpscr, scr); + print("\n"); + /* we should now be able to execute VFP-style FP instr'ns natively */ +} + +static void +resetusb(void) +{ + int bound; + Uhh *uhh; + Usbotg *otg; + Usbtll *tll; + + iprint("resetting usb: otg..."); + otg = (Usbotg *)PHYSUSBOTG; + otg->otgsyscfg = Softreset; /* see omap35x errata 3.1.1.144 */ + coherence(); + resetwait(&otg->otgsyssts); + otg->otgsyscfg |= Sidle | Midle; + coherence(); + + iprint("uhh..."); + uhh = (Uhh *)PHYSUHH; + uhh->sysconfig |= Softreset; + coherence(); + resetwait(&uhh->sysstatus); + for (bound = 400*Mhz; !(uhh->sysstatus & Resetdone) && bound > 0; + bound--) + ; + uhh->sysconfig |= Sidle | Midle; + + /* + * using the TLL seems to be an optimisation when talking + * to another identical SoC, thus not very useful, so + * force PHY (ULPI) mode. + */ + /* this bit is normally off when we get here */ + uhh->hostconfig &= ~P1ulpi_bypass; + coherence(); + if (uhh->hostconfig & P1ulpi_bypass) + iprint("utmi (tll) mode..."); /* via tll */ + else + /* external transceiver (phy), no tll */ + iprint("ulpi (phy) mode..."); + + tll = (Usbtll *)PHYSUSBTLL; + if (probeaddr(PHYSUSBTLL) >= 0) { + iprint("tll..."); + tll->sysconfig |= Softreset; + coherence(); + resetwait(&tll->sysstatus); + tll->sysconfig |= Sidle; + coherence(); + } else + iprint("no tll..."); + iprint("\n"); +} + +/* + * there are secure sdrc registers at 0x48002460 + * sdrc regs at PHYSSDRC; see spruf98c §1.2.8.2. + * set or dump l4 prot regs at PHYSL4? + */ +void +archreset(void) +{ + static int beenhere; + + if (beenhere) + return; + beenhere = 1; + + /* conservative temporary values until archconfinit runs */ + m->cpuhz = 500 * Mhz; /* beagle speed */ + m->delayloop = m->cpuhz/2000; /* initial estimate */ + +// dumpl3pr(); + prcachecfg(); + /* fight omap35x errata 2.0.1.104 */ + memset((void *)PHYSSWBOOTCFG, 0, 240); + coherence(); + + setpadmodes(); + configclks(); /* may change cpu speed */ + configgpio(); + + archconfinit(); + + resetusb(); + fpon(); +} + +void +archreboot(void) +{ + Prm *prm = (Prm *)PHYSPRMGLBL; + + iprint("archreboot: reset!\n"); + delay(20); + + prm->rstctrl |= Rstgs; + coherence(); + delay(500); + + /* shouldn't get here */ + splhi(); + iprint("awaiting reset"); + for(;;) { + delay(1000); + print("."); + } +} + +void +kbdinit(void) +{ +} + +void +lastresortprint(char *buf, long bp) +{ + iprint("%.*s", (int)bp, buf); /* nothing else seems to work */ +} + +static void +scmdump(ulong addr, int shorts) +{ + ushort reg; + ushort *ptr; + + ptr = (ushort *)addr; + print("scm regs:\n"); + while (shorts-- > 0) { + reg = *ptr++; + print("%#p: %#ux\tinputenable %d pulltypeselect %d " + "pulludenable %d muxmode %d\n", + ptr, reg, (reg>>8) & 1, (reg>>4) & 1, (reg>>3) & 1, + reg & 7); + } +} + +char *cputype2name(char *buf, int size); + +void +cpuidprint(void) +{ + char name[64]; + + cputype2name(name, sizeof name); + delay(250); /* let uart catch up */ + iprint("cpu%d: %lldMHz ARM %s\n", m->machno, m->cpuhz / Mhz, name); +} + +static void +missing(ulong addr, char *name) +{ + static int firstmiss = 1; + + if (probeaddr(addr) >= 0) + return; + if (firstmiss) { + iprint("missing:"); + firstmiss = 0; + } else + iprint(",\n\t"); + iprint(" %s at %#lux", name, addr); +} + +/* verify that all the necessary device registers are accessible */ +void +chkmissing(void) +{ + delay(20); + missing(PHYSSCM, "scm"); + missing(KZERO, "dram"); + missing(PHYSL3, "l3 config"); + missing(PHYSINTC, "intr ctlr"); + missing(PHYSTIMER1, "timer1"); + missing(PHYSCONS, "console uart2"); + missing(PHYSUART0, "uart0"); + missing(PHYSUART1, "uart1"); + missing(PHYSETHER, "smc9221"); /* not on beagle */ + missing(PHYSUSBOTG, "usb otg"); + missing(PHYSUHH, "usb uhh"); + missing(PHYSOHCI, "usb ohci"); + missing(PHYSEHCI, "usb ehci"); + missing(PHYSSDMA, "dma"); + missing(PHYSWDOG, "watchdog timer"); + missing(PHYSUSBTLL, "usb tll"); + iprint("\n"); + delay(20); +} + +void +archflashwp(Flash*, int) +{ +} + +/* + * for ../port/devflash.c:/^flashreset + * retrieve flash type, virtual base and length and return 0; + * return -1 on error (no flash) + */ +int +archflashreset(int bank, Flash *f) +{ + if(bank != 0) + return -1; + /* + * this is set up for the igepv2 board. + * if the beagleboard ever works, we'll have to sort this out. + */ + f->type = "onenand"; + f->addr = (void*)PHYSNAND; /* mapped here by archreset */ + f->size = 0; /* done by probe */ + f->width = 1; + f->interleave = 0; + return 0; +} diff --git a/sys/src/9/omap/arm.h b/sys/src/9/omap/arm.h new file mode 100755 index 000000000..12443d849 --- /dev/null +++ b/sys/src/9/omap/arm.h @@ -0,0 +1,246 @@ +/* + * arm-specific definitions for cortex-a8 + * these are used in C and assembler + * + * `cortex' refers specifically to the cortex-a8. + */ + +/* + * Program Status Registers + */ +#define PsrMusr 0x00000010 /* mode */ +#define PsrMfiq 0x00000011 +#define PsrMirq 0x00000012 +#define PsrMsvc 0x00000013 /* `protected mode for OS' */ +#define PsrMmon 0x00000016 /* `secure monitor' (trustzone hyper) */ +#define PsrMabt 0x00000017 +#define PsrMund 0x0000001B +#define PsrMsys 0x0000001F /* `privileged user mode for OS' (trustzone) */ +#define PsrMask 0x0000001F + +#define PsrDfiq 0x00000040 /* disable FIQ interrupts */ +#define PsrDirq 0x00000080 /* disable IRQ interrupts */ + +#define PsrV 0x10000000 /* overflow */ +#define PsrC 0x20000000 /* carry/borrow/extend */ +#define PsrZ 0x40000000 /* zero */ +#define PsrN 0x80000000 /* negative/less than */ + +/* + * Coprocessors + */ +#define CpFP 10 /* float FP, VFP cfg. */ +#define CpDFP 11 /* double FP */ +#define CpSC 15 /* System Control */ + +/* + * Primary (CRn) CpSC registers. + */ +#define CpID 0 /* ID and cache type */ +#define CpCONTROL 1 /* miscellaneous control */ +#define CpTTB 2 /* Translation Table Base(s) */ +#define CpDAC 3 /* Domain Access Control */ +#define CpFSR 5 /* Fault Status */ +#define CpFAR 6 /* Fault Address */ +#define CpCACHE 7 /* cache/write buffer control */ +#define CpTLB 8 /* TLB control */ +#define CpCLD 9 /* L2 Cache Lockdown, op1==1 */ +#define CpTLD 10 /* TLB Lockdown, with op2 */ +#define CpVECS 12 /* vector bases, op1==0, Crm==0, op2s (cortex) */ +#define CpPID 13 /* Process ID */ +#define CpDTLB 15 /* TLB, L1 cache stuff (cortex) */ + +/* + * CpTTB op1==0, Crm==0 opcode2 values. + */ +#define CpTTB0 0 +#define CpTTB1 1 /* cortex */ +#define CpTTBctl 2 /* cortex */ + +/* + * CpID Secondary (CRm) registers. + */ +#define CpIDidct 0 + +/* + * CpID op1==0 opcode2 fields. + * the cortex has more op1 codes for cache size, etc. + */ +#define CpIDid 0 /* main ID */ +#define CpIDct 1 /* cache type */ +#define CpIDtlb 3 /* tlb type (cortex) */ +#define CpIDmpid 5 /* multiprocessor id (cortex) */ + +/* CpIDid op1 values */ +#define CpIDcsize 1 /* cache size (cortex) */ +#define CpIDcssel 2 /* cache size select (cortex) */ + +/* + * CpCONTROL op2 codes, op1==0, Crm==0. + */ +#define CpMainctl 0 +#define CpAuxctl 1 +#define CpCPaccess 2 + +/* + * CpCONTROL: op1==0, CRm==0, op2==CpMainctl. + * main control register. + * cortex/armv7 has more ops and CRm values. + */ +#define CpCmmu 0x00000001 /* M: MMU enable */ +#define CpCalign 0x00000002 /* A: alignment fault enable */ +#define CpCdcache 0x00000004 /* C: data cache on */ +#define CpCsbo (3<<22|1<<18|1<<16|017<<3) /* must be 1 (armv7) */ +#define CpCsbz (CpCtre|1<<26|CpCve|1<<15|7<<7) /* must be 0 (armv7) */ +#define CpCsw (1<<10) /* SW: SWP(B) enable (deprecated in v7) */ +#define CpCpredict 0x00000800 /* Z: branch prediction (armv7) */ +#define CpCicache 0x00001000 /* I: instruction cache on */ +#define CpChv 0x00002000 /* V: high vectors */ +#define CpCrr (1<<14) /* RR: round robin vs random cache replacement */ +#define CpCha (1<<17) /* HA: hw access flag enable */ +#define CpCdz (1<<19) /* DZ: divide by zero fault enable */ +#define CpCfi (1<<21) /* FI: fast intrs */ +#define CpCve (1<<24) /* VE: intr vectors enable */ +#define CpCee (1<<25) /* EE: exception endianness */ +#define CpCnmfi (1<<27) /* NMFI: non-maskable fast intrs. */ +#define CpCtre (1<<28) /* TRE: TEX remap enable */ +#define CpCafe (1<<29) /* AFE: access flag (ttb) enable */ + +/* + * CpCONTROL: op1==0, CRm==0, op2==CpAuxctl. + * Auxiliary control register on cortex at least. + */ +#define CpACcachenopipe (1<<20) /* don't pipeline cache maint. */ +#define CpACcp15serial (1<<18) /* serialise CP1[45] ops. */ +#define CpACcp15waitidle (1<<17) /* CP1[45] wait-on-idle */ +#define CpACcp15pipeflush (1<<16) /* CP1[45] flush pipeline */ +#define CpACneonissue1 (1<<12) /* neon single issue */ +#define CpACldstissue1 (1<<11) /* force single issue ld, st */ +#define CpACissue1 (1<<10) /* force single issue */ +#define CpACnobsm (1<<7) /* no branch size mispredicts */ +#define CpACibe (1<<6) /* cp15 invalidate & btb enable */ +#define CpACl1neon (1<<5) /* cache neon (FP) data in L1 cache */ +#define CpACasa (1<<4) /* enable speculative accesses */ +#define CpACl1pe (1<<3) /* l1 cache parity enable */ +#define CpACl2en (1<<1) /* l2 cache enable; default 1 */ +/* + * CpCONTROL Secondary (CRm) registers and opcode2 fields. + */ +#define CpCONTROLscr 1 + +#define CpSCRscr 0 + +/* + * CpCACHE Secondary (CRm) registers and opcode2 fields. op1==0. + * In ARM-speak, 'flush' means invalidate and 'clean' means writeback. + */ +#define CpCACHEintr 0 /* interrupt (op2==4) */ +#define CpCACHEisi 1 /* inner-sharable I cache (v7) */ +#define CpCACHEpaddr 4 /* 0: phys. addr (cortex) */ +#define CpCACHEinvi 5 /* instruction, branch table */ +#define CpCACHEinvd 6 /* data or unified */ +// #define CpCACHEinvu 7 /* unified (not on cortex) */ +#define CpCACHEva2pa 8 /* va -> pa translation (cortex) */ +#define CpCACHEwb 10 /* writeback */ +#define CpCACHEinvdse 11 /* data or unified by mva */ +#define CpCACHEwbi 14 /* writeback+invalidate */ + +#define CpCACHEall 0 /* entire (not for invd nor wb(i) on cortex) */ +#define CpCACHEse 1 /* single entry */ +#define CpCACHEsi 2 /* set/index (set/way) */ +#define CpCACHEtest 3 /* test loop */ +#define CpCACHEwait 4 /* wait (prefetch flush on cortex) */ +#define CpCACHEdmbarr 5 /* wb only (cortex) */ +#define CpCACHEflushbtc 6 /* flush branch-target cache (cortex) */ +#define CpCACHEflushbtse 7 /* ⋯ or just one entry in it (cortex) */ + +/* + * CpTLB Secondary (CRm) registers and opcode2 fields. + */ +#define CpTLBinvi 5 /* instruction */ +#define CpTLBinvd 6 /* data */ +#define CpTLBinvu 7 /* unified */ + +#define CpTLBinv 0 /* invalidate all */ +#define CpTLBinvse 1 /* invalidate single entry */ +#define CpTBLasid 2 /* by ASID (cortex) */ + +/* + * CpCLD Secondary (CRm) registers and opcode2 fields for op1==0. (cortex) + */ +#define CpCLDena 12 /* enables */ +#define CpCLDcyc 13 /* cycle counter */ +#define CpCLDuser 14 /* user enable */ + +#define CpCLDenapmnc 0 +#define CpCLDenacyc 1 + +/* + * CpCLD Secondary (CRm) registers and opcode2 fields for op1==1. + */ +#define CpCLDl2 0 /* l2 cache */ + +#define CpCLDl2aux 2 /* auxiliary control */ + +/* + * l2 cache aux. control + */ +#define CpCl2ecc (1<<28) /* use ecc, not parity */ +#define CpCl2noldforw (1<<27) /* no ld forwarding */ +#define CpCl2nowrcomb (1<<25) /* no write combining */ +#define CpCl2nowralldel (1<<24) /* no write allocate delay */ +#define CpCl2nowrallcomb (1<<23) /* no write allocate combine */ +#define CpCl2nowralloc (1<<22) /* no write allocate */ +#define CpCl2eccparity (1<<21) /* enable ecc or parity */ +#define CpCl2inner (1<<16) /* inner cacheability */ +/* other bits are tag ram & data ram latencies */ + +/* + * CpTLD Secondary (CRm) registers and opcode2 fields. + */ +#define CpTLDlock 0 /* TLB lockdown registers */ +#define CpTLDpreload 1 /* TLB preload */ + +#define CpTLDi 0 /* TLB instr. lockdown reg. */ +#define CpTLDd 1 /* " data " " */ + +/* + * CpVECS Secondary (CRm) registers and opcode2 fields. + */ +#define CpVECSbase 0 + +#define CpVECSnorm 0 /* (non-)secure base addr */ +#define CpVECSmon 1 /* secure monitor base addr */ + +/* + * MMU page table entries. + * Mbz (0x10) bit is implementation-defined and must be 0 on the cortex. + */ +#define Mbz (0<<4) +#define Fault 0x00000000 /* L[12] pte: unmapped */ + +#define Coarse (Mbz|1) /* L1 */ +#define Section (Mbz|2) /* L1 1MB */ +#define Fine (Mbz|3) /* L1 */ + +#define Large 0x00000001 /* L2 64KB */ +#define Small 0x00000002 /* L2 4KB */ +#define Tiny 0x00000003 /* L2 1KB: not in v7 */ +#define Buffered 0x00000004 /* L[12]: write-back not -thru */ +#define Cached 0x00000008 /* L[12] */ +#define Dom0 0 + +#define Noaccess 0 /* AP, DAC */ +#define Krw 1 /* AP */ +/* armv7 deprecates AP[2] == 1 & AP[1:0] == 2 (Uro), prefers 3 (new in v7) */ +#define Uro 2 /* AP */ +#define Urw 3 /* AP */ +#define Client 1 /* DAC */ +#define Manager 3 /* DAC */ + +#define AP(n, v) F((v), ((n)*2)+4, 2) +#define L1AP(ap) (AP(3, (ap))) +#define L2AP(ap) (AP(0, (ap))) /* armv7 */ +#define DAC(n, v) F((v), (n)*2, 2) + +#define HVECTORS 0xffff0000 diff --git a/sys/src/9/omap/arm.s b/sys/src/9/omap/arm.s new file mode 100755 index 000000000..829e36234 --- /dev/null +++ b/sys/src/9/omap/arm.s @@ -0,0 +1,95 @@ +/* + * omap3530 machine assist, definitions + * cortex-a8 processor + * + * loader uses R11 as scratch. + */ + +#include "mem.h" +#include "arm.h" + +#undef B /* B is for 'botch' */ + +#define KADDR(pa) (KZERO | ((pa) & ~KSEGM)) +#define PADDR(va) (PHYSDRAM | ((va) & ~KSEGM)) + +#define L1X(va) (((((va))>>20) & 0x0fff)<<2) + +#define MACHADDR (L1-MACHSIZE) + +#define PTEDRAM (Dom0|L1AP(Krw)|Section|Cached|Buffered) +#define PTEIO (Dom0|L1AP(Krw)|Section) + +#define DOUBLEMAPMBS 256 /* megabytes of low dram to double-map */ + +/* steps on R0 */ +#define DELAY(label, mloops) \ + MOVW $((mloops)*1000000), R0; \ +label: \ + SUB.S $1, R0; \ + BNE label + +/* wave at the user; clobbers R0, R1 & R6; needs R12 (SB) set */ +#define WAVE(c) \ + BARRIERS; \ + MOVW $(c), R1; \ + MOVW $PHYSCONS, R6; \ + MOVW R1, (R6); \ + BARRIERS + +/* + * new instructions + */ + +#define SMC WORD $0xe1600070 /* low 4-bits are call # (trustzone) */ +/* flush branch-target cache; zeroes R0 (cortex) */ +#define FLBTC \ + MOVW $0, R0; \ + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEflushbtc +/* flush one entry of the branch-target cache, va in R0 (cortex) */ +#define FLBTSE \ + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEflushbtse + +/* arm v7 arch defines these */ +#define WFI WORD $0xe320f003 /* wait for interrupt */ +#define DMB WORD $0xf57ff05f /* data mem. barrier; last f = SY */ +#define DSB WORD $0xf57ff04f /* data synch. barrier; last f = SY */ +#define ISB WORD $0xf57ff06f /* instr. sync. barrier; last f = SY */ +#define NOOP WORD $0xe320f000 +#define CLZ(s, d) WORD $(0xe16f0f10 | (d) << 12 | (s)) /* count leading 0s */ +#define CPSIE WORD $0xf1080080 /* intr enable: zeroes I bit */ +#define CPSID WORD $0xf10c0080 /* intr disable: sets I bit */ + +/* floating point */ +#define VMRS(fp, cpu) WORD $(0xeef00a10 | (fp)<<16 | (cpu)<<12) /* FP → arm */ +#define VMSR(cpu, fp) WORD $(0xeee00a10 | (fp)<<16 | (cpu)<<12) /* arm → FP */ + +/* + * a popular code sequence used to write a pte for va is: + * + * MOVW R(n), TTB[LnX(va)] + * // clean the cache line + * DSB + * // invalidate tlb entry for va + * FLBTC + * DSB + * PFF (now ISB) + */ +/* zeroes R0 */ +#define BARRIERS FLBTC; DSB; ISB + +/* + * invoked with PTE bits in R2, pa in R3, PTE pointed to by R4. + * fill PTE pointed to by R4 and increment R4 past it. + * increment R3 by a MB. clobbers R1. + */ +#define FILLPTE() \ + ORR R3, R2, R1; /* pte bits in R2, pa in R3 */ \ + MOVW R1, (R4); \ + ADD $4, R4; /* bump PTE address */ \ + ADD $MiB, R3; /* bump pa */ \ + +/* zero PTE pointed to by R4 and increment R4 past it. assumes R0 is 0. */ +#define ZEROPTE() \ + MOVW R0, (R4); \ + ADD $4, R4; /* bump PTE address */ diff --git a/sys/src/9/omap/beagle b/sys/src/9/omap/beagle new file mode 100755 index 000000000..887395443 --- /dev/null +++ b/sys/src/9/omap/beagle @@ -0,0 +1,82 @@ +# beagle, igepv2, gumstix overo omap35 boards +dev + root + cons + env + pipe + proc + mnt + srv + dup + arch + ssl + tls + bridge log + sdp thwack unthwack + cap + kprof +# aoe +# sd + fs +# flash + + ether netif + ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno + + draw screen + dss + kbmap + kbin + mouse + + uart +# usb + +link + archomap + ethermedium +# flashbeagle ecc +# flashigep + loopbackmedium + netdevmedium + + ether9221 +## avoid tickling errata 3.1.1.183 +## usbohci +# usbehci usbehciomap + +ip + tcp + udp + ipifc + icmp + icmp6 + ipmux + gre + esp + +misc + rdb + coproc + dma + mouse +# sdaoe sdscsi + softfpu + syscall + uarti8250 + ucalloc + ucallocb + +port + int cpuserver = 1; + int i8250freq = 3686000; + +boot cpu + tcp + +bootdir + boot$CONF.out boot + /arm/bin/ip/ipconfig ipconfig + /arm/bin/auth/factotum factotum + /arm/bin/usb/usbd + nvram diff --git a/sys/src/9/omap/cache.v7.s b/sys/src/9/omap/cache.v7.s new file mode 100755 index 000000000..d6a6eef6e --- /dev/null +++ b/sys/src/9/omap/cache.v7.s @@ -0,0 +1,208 @@ +/* + * cortex arm arch v7 cache flushing and invalidation + * shared by l.s and rebootcode.s + */ + +TEXT cacheiinv(SB), $-4 /* I invalidate */ + MOVW $0, R0 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall /* ok on cortex */ + 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(CpIDid), 0 + ISB + RET + + /* return cache sizes */ +TEXT getwayssets(SB), $-4 + MRC CpSC, CpIDcsize, R0, C(CpID), C(CpIDid), 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 + CMP $0, R2 /* PC segment should be non-zero on omap */ + BEQ buggery + 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(CpIDid), 0 /* set cache size select */ + ISB + MRC CpSC, CpIDcsize, R0, C(CpID), C(CpIDid), 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 + +buggery: +WAVE('?') + MOVW PC, R0 +// B pczeroseg(SB) + RET diff --git a/sys/src/9/omap/clock.c b/sys/src/9/omap/clock.c new file mode 100755 index 000000000..eaab1e270 --- /dev/null +++ b/sys/src/9/omap/clock.c @@ -0,0 +1,489 @@ +/* + * omap3530 clocks + * + * timers count up to zero. + * + * the source clock signals for the timers are sometimes selectable. for + * WDTIMER[23] and GPTIMER12, it's always the 32kHz clock. for the + * others, it can be the 32kHz clock or the system clock. we use only + * WDTIMER2 and GPTIMER[12], and configure GPTIMER[12] in archomap.c to + * use the 32kHZ clock. WDTIMER1 is not accessible to us on GP + * (general-purpose) omaps. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "arm.h" + +enum { + Debug = 0, + + Tn0 = PHYSTIMER1, + Tn1 = PHYSTIMER2, + + /* irq 36 is watchdog timer module 3 overflow */ + Tn0irq = 37, /* base IRQ for all timers */ + + Freebase = 1, /* base of free-running timer */ + + /* + * clock is 32K (32,768) Hz, so one tick is 30.517µs, + * so 327.68 ticks is 10ms, 32.768 ticks is 1ms. + */ + Clockfreqbase = 32 * 1024, /* base rate in Hz */ + Tcycles = Clockfreqbase / HZ, /* cycles per clock tick */ + + MinPeriod = (Tcycles / 100 < 2? 2: Tcycles / 100), + MaxPeriod = Tcycles, + + Dogtimeout = 20 * Clockfreqbase, /* was 4 s.; must be ≤ 21 s. */ +}; + +enum { + /* ticpcfg bits */ + Noidle = 1<<3, + Softreset = 1<<1, + + /* tistat bits */ + Resetdone = 1<<0, + + /* tisr/tier bits */ + Ovf_it = 1<<1, /* gp: overflow intr */ + Mat_it = 1<<0, /* gp: match intr */ + Wdovf_it = 1<<0, /* wdog: overflow intr */ + + /* tclr bits */ + Ar = 1<<1, /* gp only: autoreload mode overflow */ + St = 1<<0, /* gp only: start the timer */ +}; + +/* omap35x timer registers */ +typedef struct Timerregs Timerregs; +struct Timerregs { + /* common to all timers, gp and watchdog */ + uchar pad0[0x10]; + ulong ticpcfg; + ulong tistat; /* ro: low bit: reset done */ + ulong tisr; + ulong tier; + ulong twer; + ulong tclr; + ulong tcrr; /* counter: cycles to zero */ + ulong tldr; + ulong ttgr; /* trigger */ + ulong twps; /* ro: write posted pending */ + + /* gp timers only, unused by us */ + ulong tmar; /* value to compare with counter */ + ulong tcar1; /* ro */ + ulong tsicr; + ulong tcar2; /* ro */ + union { + ulong tpir; /* gp: 1 ms tick generation: +ve */ + ulong wspr; /* wdog: start/stop control */ + }; + ulong tnir; /* 1 ms tick generation: -ve */ + ulong tcvr; /* 1 ms tick generation: next counter value */ + ulong tocr; /* intr mask for n ticks */ + ulong towr; +}; + +static int ticks; /* for sanity checking; m->ticks doesn't always get called */ +static Lock clklck; + +static ulong rdcycles(void), rdbaseticks(void); + +/* write a watchdog timer's start/stop register */ +static void +wdogwrss(Timerregs *tn, ulong val) +{ + while (tn->twps & (1 << 4)) /* pending write to start/stop reg? */ + ; + tn->wspr = val; + coherence(); + while (tn->twps & (1 << 4)) /* pending write to start/stop reg? */ + ; +} + +static void +resetwait(Timerregs *tn) +{ + long bound; + + for (bound = 400*Mhz; !(tn->tistat & Resetdone) && bound > 0; bound--) + ; + if (bound <= 0) + iprint("clock reset didn't complete\n"); +} + + +static void +wdogoff(Timerregs *tn) +{ + resetwait(tn); + + wdogwrss(tn, 0xaaaa); /* magic off sequence */ + wdogwrss(tn, 0x5555); + + tn->tldr = 1; + coherence(); + tn->tcrr = 1; /* paranoia */ + coherence(); +} + +static void wdogassure(void); + +static void +wdogon(Timerregs *tn) +{ + static int beenhere; + + resetwait(tn); + tn->tldr = -Dogtimeout; + tn->tcrr = -Dogtimeout; + coherence(); + wdogwrss(tn, 0xbbbb); /* magic on sequence */ + wdogwrss(tn, 0x4444); /* magic on sequence */ + + if (!beenhere) { + beenhere = 1; + /* touching the dog is not quick, so do it infrequently */ + addclock0link(wdogassure, HZ); + } +} + +static void +wdogassure(void) /* reset the watch dog's counter */ +{ + Timerregs *tn; + + tn = (Timerregs *)PHYSWDOG; + wdogoff(tn); + + tn->tcrr = -Dogtimeout; + coherence(); + + wdogon(tn); +} + +static void +clockintr(Ureg* ureg, void *arg) +{ + Timerregs *tn; + static int nesting; + + ticks++; + coherence(); + + if (nesting == 0) { /* if the clock interrupted itself, bail out */ + ++nesting; + timerintr(ureg, 0); + --nesting; + } + + tn = arg; + tn->tisr = Ovf_it; /* dismiss the interrupt */ + coherence(); +} + +static void +clockreset(Timerregs *tn) +{ + if (probeaddr((uintptr)&tn->ticpcfg) < 0) + panic("no clock at %#p", tn); + tn->ticpcfg = Softreset | Noidle; + coherence(); + resetwait(tn); + tn->tier = tn->tclr = 0; + coherence(); +} + +/* stop clock interrupts and disable the watchdog timer */ +void +clockshutdown(void) +{ + clockreset((Timerregs *)PHYSWDT2); + wdogoff((Timerregs *)PHYSWDT2); + clockreset((Timerregs *)PHYSWDT3); + wdogoff((Timerregs *)PHYSWDT3); + + clockreset((Timerregs *)Tn0); + clockreset((Timerregs *)Tn1); +} + +enum { + Instrs = 10*Mhz, +}; + +static long +issue1loop(void) +{ + register int i; + long st; + + st = rdbaseticks(); + i = Instrs; + do { + --i; --i; --i; --i; --i; + --i; --i; --i; --i; + } while(--i >= 0); + return rdbaseticks() - st; +} + +static long +issue2loop(void) +{ + register int i, j; + long st; + + st = rdbaseticks(); + i = Instrs / 2; + j = 0; + do { + --i; --j; --i; --j; + --i; --j; --i; --j; + --j; + } while(--i >= 0); + return rdbaseticks() - st; +} + +/* estimate instructions/s. using 32kHz clock */ +static void +guessmips(long (*loop)(void), char *lab) +{ + int s; + long tcks; + + do { + s = splhi(); + tcks = loop(); + splx(s); + if (tcks < 0) + iprint("again..."); + } while (tcks < 0); + /* + * Instrs instructions took tcks ticks @ Clockfreqbase Hz. + */ + s = ((vlong)Clockfreqbase * Instrs) / tcks / 1000000; + if (Debug) + iprint("%ud mips (%s-issue)", s, lab); + USED(s); +} + +void +clockinit(void) +{ + int i, s; + Timerregs *tn; + + clockshutdown(); + + /* turn cycle counter on */ + cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31); + + /* turn all counters on and clear the cycle counter */ + cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1); + + /* let users read the cycle counter directly */ + cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1); + + ilock(&clklck); + m->fastclock = 1; + m->ticks = ticks = 0; + + /* + * T0 is a freerunning timer (cycle counter); it wraps, + * automatically reloads, and does not dispatch interrupts. + */ + tn = (Timerregs *)Tn0; + tn->tcrr = Freebase; /* count up to 0 */ + tn->tldr = Freebase; + coherence(); + tn->tclr = Ar | St; + iunlock(&clklck); + + /* + * T1 is the interrupting timer and does not participate + * in measuring time. It is initially set to HZ. + */ + tn = (Timerregs *)Tn1; + irqenable(Tn0irq+1, clockintr, tn, "clock"); + ilock(&clklck); + tn->tcrr = -Tcycles; /* approx.; count up to 0 */ + tn->tldr = -Tcycles; + coherence(); + tn->tclr = Ar | St; + coherence(); + tn->tier = Ovf_it; + coherence(); + iunlock(&clklck); + + /* + * verify sanity of timer1 + */ + s = spllo(); /* risky */ + for (i = 0; i < 5 && ticks == 0; i++) { + delay(10); + cachedwbinvse(&ticks, sizeof ticks); + } + splx(s); + if (ticks == 0) { + if (tn->tcrr == 0) + panic("clock not interrupting"); + else if (tn->tcrr == tn->tldr) + panic("clock not ticking at all"); +#ifdef PARANOID + else + panic("clock running very slowly"); +#endif + } + + guessmips(issue1loop, "single"); + if (Debug) + iprint(", "); + guessmips(issue2loop, "dual"); + if (Debug) + iprint("\n"); + + /* + * m->delayloop should be the number of delay loop iterations + * needed to consume 1 ms. 2 is min. instructions in the delay loop. + */ + m->delayloop = m->cpuhz / (1000 * 2); +// iprint("m->delayloop = %lud\n", m->delayloop); + + /* + * desynchronize the processor clocks so that they all don't + * try to resched at the same time. + */ + delay(m->machno*2); +} + +void +watchdoginit(void) +{ + wdogassure(); +} + +ulong +µs(void) +{ + return fastticks2us(fastticks(nil)); +} + +void +timerset(Tval next) +{ + long offset; + Timerregs *tn = (Timerregs *)Tn1; + static Lock setlck; + + ilock(&setlck); + offset = next - fastticks(nil); + if(offset < MinPeriod) + offset = MinPeriod; + else if(offset > MaxPeriod) + offset = MaxPeriod; + tn->tcrr = -offset; + coherence(); + iunlock(&setlck); +} + +static ulong +rdcycles(void) +{ + ulong v; + + /* reads 32-bit cycle counter (counting up) */ + v = cprdsc(0, CpCLD, CpCLDcyc, 0); + /* keep it positive; prevent m->fastclock ever going to 0 */ + return v == 0? 1: v; +} + +static ulong +rdbaseticks(void) +{ + ulong v; + + v = ((Timerregs *)Tn0)->tcrr; /* tcrr should be counting up */ + /* keep it positive; prevent m->fastclock ever going to 0 */ + return v == 0? 1: v; +} + +ulong +perfticks(void) +{ + return rdcycles(); +} + +long +lcycles(void) +{ + return perfticks(); +} + +/* + * until 5[cal] inline vlong ops, avoid them where possible, + * they are currently slow function calls. + */ +typedef union Counter Counter; +union Counter { + uvlong uvl; + struct { /* little-endian */ + ulong low; + ulong high; + }; +}; + +enum { + Fastvlongops = 0, +}; + +uvlong +fastticks(uvlong *hz) +{ + Counter now, sclnow; + + if(hz) + *hz = m->cpuhz; + ilock(&clklck); + if (m->ticks > HZ/10 && m->fastclock == 0) + panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux", + m->ticks, m->fastclock); + + now.uvl = m->fastclock; + now.low = rdcycles(); + if(now.uvl < m->fastclock) /* low bits must have wrapped */ + now.high++; + m->fastclock = now.uvl; + coherence(); + + sclnow.uvl = now.uvl; + iunlock(&clklck); + return sclnow.uvl; +} + +void +microdelay(int l) +{ + int i; + + l = l * (vlong)m->delayloop / 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +void +delay(int l) +{ + ulong i, j; + + j = m->delayloop; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} diff --git a/sys/src/9/omap/coproc.c b/sys/src/9/omap/coproc.c new file mode 100755 index 000000000..411f0fef7 --- /dev/null +++ b/sys/src/9/omap/coproc.c @@ -0,0 +1,133 @@ +/* + * arm co-processors + * CP15 (system control) is the one that gets used the most in practice. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "arm.h" + +#define MAP2PCSPACE(va, pc) ((uintptr)(va) & ~KSEGM | (pc) & KSEGM) + +enum { + /* alternates: 0xe12fff1e BX (R14); last e is R14 */ + /* 0xe28ef000 B 0(R14); second e is R14 (ken) */ + Retinst = 0xe1a0f00e, /* MOV R14, R15 */ +}; + +void +cpwr(int cp, int op1, int crn, int crm, int op2, ulong val) +{ + volatile ulong instr[2]; + void *pcaddr; + void (*fp)(ulong); + + op1 &= 7; + op2 &= 7; + crn &= 017; + crm &= 017; + cp &= 017; + /* MCR. Rt will be R0. */ + instr[0] = 0xee000010 | + op1 << 21 | crn << 16 | cp << 8 | op2 << 5 | crm; + instr[1] = Retinst; + + pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&cp)); + cachedwbse(pcaddr, sizeof instr); + cacheiinv(); + + fp = (void (*)(ulong))pcaddr; + (*fp)(val); + coherence(); +} + +void +cpwrsc(int op1, int crn, int crm, int op2, ulong val) +{ + cpwr(CpSC, op1, crn, crm, op2, val); +} + +ulong +cprd(int cp, int op1, int crn, int crm, int op2) +{ + volatile ulong instr[2]; + void *pcaddr; + ulong (*fp)(void); + + op1 &= 7; + op2 &= 7; + crn &= 017; + crm &= 017; + /* + * MRC. return value will be in R0, which is convenient. + * Rt will be R0. + */ + instr[0] = 0xee100010 | + op1 << 21 | crn << 16 | cp << 8 | op2 << 5 | crm; + instr[1] = Retinst; + + pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&cp)); + cachedwbse(pcaddr, sizeof instr); + cacheiinv(); + + fp = (ulong (*)(void))pcaddr; + return (*fp)(); +} + +ulong +cprdsc(int op1, int crn, int crm, int op2) +{ + return cprd(CpSC, op1, crn, crm, op2); +} + +/* floating point */ + +ulong +fprd(int fpreg) +{ + volatile ulong instr[2]; + void *pcaddr; + ulong (*fp)(void); + + fpreg &= 017; + /* + * VMRS. return value will be in R0, which is convenient. + * Rt will be R0. + */ + instr[0] = 0xeef00a10 | fpreg << 16 | 0 << 12; + instr[1] = Retinst; + coherence(); + + pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&fpreg)); + cachedwbse(pcaddr, sizeof instr); + cacheiinv(); + + fp = (ulong (*)(void))pcaddr; + return (*fp)(); +} + +void +fpwr(int fpreg, ulong val) +{ + volatile ulong instr[2]; + void *pcaddr; + void (*fp)(ulong); + + fpreg &= 017; + /* VMSR. Rt will be R0. */ + instr[0] = 0xeee00a10 | fpreg << 16 | 0 << 12; + instr[1] = Retinst; + coherence(); + + pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&fpreg)); + cachedwbse(pcaddr, sizeof instr); + cacheiinv(); + + fp = (void (*)(ulong))pcaddr; + (*fp)(val); + coherence(); +} diff --git a/sys/src/9/omap/dat.h b/sys/src/9/omap/dat.h new file mode 100755 index 000000000..37114691d --- /dev/null +++ b/sys/src/9/omap/dat.h @@ -0,0 +1,303 @@ +/* + * Time. + * + * HZ should divide 1000 evenly, ideally. + * 100, 125, 200, 250 and 333 are okay. + */ +#define HZ 100 /* clock frequency */ +#define MS2HZ (1000/HZ) /* millisec per clock tick */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +enum { + Mhz = 1000 * 1000, +}; + +/* + * More accurate time + */ +#define MS2TMR(t) ((ulong)(((uvlong)(t) * m->cpuhz)/1000)) +#define US2TMR(t) ((ulong)(((uvlong)(t) * m->cpuhz)/1000000)) + +/* + * we ignore the first 2 uarts on the omap3530 (see uarti8250.c) and use the + * third one but call it 0. + */ +#define CONSOLE 0 + +typedef struct Conf Conf; +typedef struct Confmem Confmem; +typedef struct FPsave FPsave; +typedef struct ISAConf ISAConf; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Memcache Memcache; +typedef struct MMMU MMMU; +typedef struct Mach Mach; +typedef u32int Mreg; /* Msr - bloody UART */ +typedef struct Notsave Notsave; +typedef struct Page Page; +typedef struct PhysUart PhysUart; +typedef struct PMMU PMMU; +typedef struct Proc Proc; +typedef u32int PTE; +typedef struct Uart Uart; +typedef struct Ureg Ureg; +typedef uvlong Tval; + +#pragma incomplete Ureg + +#define MAXSYSARG 5 /* for mount(fd, mpt, flag, arg, srv) */ + +/* + * parameters for sysproc.c + */ +#define AOUT_MAGIC (E_MAGIC) + +struct Lock +{ + ulong key; + u32int sr; + uintptr pc; + Proc* p; + Mach* m; + int isilock; +}; + +struct Label +{ + uintptr sp; + uintptr pc; +}; + +/* + * emulated floating point + */ +struct FPsave +{ + ulong status; + ulong control; + ulong regs[8][3]; + + int fpstate; +}; + +/* + * FPsave.status + */ +enum +{ + FPinit, + FPactive, + FPinactive, +}; + +struct Confmem +{ + uintptr base; + usize npage; + uintptr limit; + uintptr kbase; + uintptr klimit; +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + Confmem mem[1]; /* physical memory */ + ulong npage; /* total physical pages of memory */ + usize upages; /* user page pool */ + ulong copymode; /* 0 is copy on write, 1 is copy on reference */ + ulong ialloc; /* max interrupt time allocation in bytes */ + ulong pipeqsize; /* size in bytes of pipe queues */ + ulong nimage; /* number of page cache image headers */ + ulong nswap; /* number of swap pages */ + int nswppo; /* max # of pageouts per segment pass */ + ulong hz; /* processor cycle freq */ + ulong mhz; + int monitor; /* flag */ +}; + +/* + * things saved in the Proc structure during a notify + */ +struct Notsave { + int emptiness; +}; + +/* + * MMU stuff in Mach. + */ +struct MMMU +{ + PTE* mmul1; /* l1 for this processor */ + int mmul1lo; + int mmul1hi; + int mmupid; +}; + +/* + * MMU stuff in proc + */ +#define NCOLOR 1 /* 1 level cache, don't worry about VCE's */ +struct PMMU +{ + Page* mmul2; + Page* mmul2cache; /* free mmu pages */ +}; + +#include "../port/portdat.h" + +struct Mach +{ + int machno; /* physical id of processor */ + uintptr splpc; /* pc of last caller to splhi */ + + Proc* proc; /* current process */ + + MMMU; + int flushmmu; /* flush current proc mmu state */ + + ulong ticks; /* of the clock since boot time */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void* alarm; /* alarms bound to this clock */ + int inclockintr; + + Proc* readied; /* for runproc */ + ulong schedticks; /* next forced context switch */ + + int cputype; + ulong delayloop; + + /* stats */ + int tlbfault; + int tlbpurge; + int pfault; + int cs; + int syscall; + int load; + int intr; + uvlong fastclock; /* last sampled value */ + uvlong inidle; /* time spent in idlehands() */ + ulong spuriousintr; + int lastintr; + int ilockdepth; + Perf perf; /* performance counters */ + + + int cpumhz; + uvlong cpuhz; /* speed of cpu */ + uvlong cyclefreq; /* Frequency of user readable cycle counter */ + + /* save areas for exceptions, hold R0-R4 */ + u32int sfiq[5]; + u32int sirq[5]; + u32int sund[5]; + u32int sabt[5]; + u32int smon[5]; /* probably not needed */ + u32int ssys[5]; + + int stack[1]; +}; + +/* + * Fake kmap. + */ +typedef void KMap; +#define VA(k) ((uintptr)(k)) +#define kmap(p) (KMap*)((p)->pa|kseg0) +#define kunmap(k) + +struct +{ + Lock; + int machs; /* bitmap of active CPUs */ + int exiting; /* shutdown */ + int ispanic; /* shutdown in response to a panic */ +}active; + +extern register Mach* m; /* R10 */ +extern register Proc* up; /* R9 */ +extern uintptr kseg0; +extern Mach* machaddr[MAXMACH]; +extern ulong memsize; +extern int normalprint; + +/* + * a parsed plan9.ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char *type; + ulong port; + int irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +#define MACHP(n) (machaddr[n]) + +/* + * Horrid. But the alternative is 'defined'. + */ +#ifdef _DBGC_ +#define DBGFLG (dbgflg[_DBGC_]) +#else +#define DBGFLG (0) +#endif /* _DBGC_ */ + +int vflag; +extern char dbgflg[256]; + +#define dbgprint print /* for now */ + +/* + * hardware info about a device + */ +typedef struct { + ulong port; + int size; +} Devport; + +struct DevConf +{ + ulong intnum; /* interrupt number */ + char *type; /* card type, malloced */ + int nports; /* Number of ports */ + Devport *ports; /* The ports themselves */ +}; + +enum { + Dcache, + Icache, + Unified, +}; + +/* characteristics of a given cache level */ +struct Memcache { + uint level; /* 1 is nearest processor, 2 further away */ + uint l1ip; /* l1 I policy */ + + uint nways; /* associativity */ + uint nsets; + uint linelen; /* bytes per cache line */ + uint setsways; + + uint log2linelen; + uint waysh; /* shifts for set/way register */ + uint setsh; +}; + +enum Dmamode { + Const, + Postincr, + Index, + Index2, +}; diff --git a/sys/src/9/omap/devarch.c b/sys/src/9/omap/devarch.c new file mode 100755 index 000000000..06b18d829 --- /dev/null +++ b/sys/src/9/omap/devarch.c @@ -0,0 +1,200 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#include "../ip/ip.h" + +enum { + Qdir = 0, + Qbase, + + Qmax = 16, +}; + +typedef long Rdwrfn(Chan*, void*, long, vlong); + +static Rdwrfn *readfn[Qmax]; +static Rdwrfn *writefn[Qmax]; + +static Dirtab archdir[Qmax] = { + ".", { Qdir, 0, QTDIR }, 0, 0555, +}; + +Lock archwlock; /* the lock is only for changing archdir */ +int narchdir = Qbase; + +/* + * Add a file to the #P listing. Once added, you can't delete it. + * You can't add a file with the same name as one already there, + * and you get a pointer to the Dirtab entry so you can do things + * like change the Qid version. Changing the Qid path is disallowed. + */ +Dirtab* +addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn) +{ + int i; + Dirtab d; + Dirtab *dp; + + memset(&d, 0, sizeof d); + strcpy(d.name, name); + d.perm = perm; + + lock(&archwlock); + if(narchdir >= Qmax){ + unlock(&archwlock); + return nil; + } + + for(i=0; iqid.path){ + case Qdir: + return devdirread(c, a, n, archdir, narchdir, devgen); + + default: + if(c->qid.path < narchdir && (fn = readfn[c->qid.path])) + return fn(c, a, n, offset); + error(Eperm); + break; + } + + return 0; +} + +static long +archwrite(Chan *c, void *a, long n, vlong offset) +{ + Rdwrfn *fn; + + if(c->qid.path < narchdir && (fn = writefn[c->qid.path])) + return fn(c, a, n, offset); + error(Eperm); + + return 0; +} + +void archinit(void); + +Dev archdevtab = { + 'P', + "arch", + + devreset, + archinit, + devshutdown, + archattach, + archwalk, + archstat, + archopen, + devcreate, + archclose, + archread, + devbread, + archwrite, + devbwrite, + devremove, + devwstat, +}; + +/* convert AddrDevid register to a string in buf and return buf */ +char * +cputype2name(char *buf, int size) +{ + seprint(buf, buf + size, "Cortex-A8"); + return buf; +} + +static long +cputyperead(Chan*, void *a, long n, vlong offset) +{ + char name[64], str[128]; + + cputype2name(name, sizeof name); + snprint(str, sizeof str, "ARM %s %llud\n", name, m->cpuhz / Mhz); + return readstr(offset, a, n, str); +} + +static long +tbread(Chan*, void *a, long n, vlong offset) +{ + char str[16]; + uvlong tb; + + cycles(&tb); + + snprint(str, sizeof(str), "%16.16llux", tb); + return readstr(offset, a, n, str); +} + +static long +nsread(Chan*, void *a, long n, vlong offset) +{ + char str[16]; + uvlong tb; + + cycles(&tb); + + snprint(str, sizeof(str), "%16.16llux", (tb/700)* 1000); + return readstr(offset, a, n, str); +} + +void +archinit(void) +{ + addarchfile("cputype", 0444, cputyperead, nil); + addarchfile("timebase",0444, tbread, nil); +// addarchfile("nsec", 0444, nsread, nil); +} diff --git a/sys/src/9/omap/devcons.c b/sys/src/9/omap/devcons.c new file mode 100755 index 000000000..f94f12d53 --- /dev/null +++ b/sys/src/9/omap/devcons.c @@ -0,0 +1,1354 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "pool.h" +#include + +void (*consdebug)(void) = nil; +void (*screenputs)(char*, int) = nil; + +Queue* kbdq; /* unprocessed console input */ +Queue* lineq; /* processed console input */ +Queue* serialoq; /* serial console output */ +Queue* kprintoq; /* console output, for /dev/kprint */ +ulong kprintinuse; /* test and set whether /dev/kprint is open */ +int iprintscreenputs = 1; + +int panicking; + +static struct +{ + QLock; + + int raw; /* true if we shouldn't process input */ + Ref ctl; /* number of opens to the control file */ + int x; /* index into line */ + char line[1024]; /* current input line */ + + int count; + int ctlpoff; + + /* a place to save up characters at interrupt time before dumping them in the queue */ + Lock lockputc; + char istage[1024]; + char *iw; + char *ir; + char *ie; +} kbd = { + .iw = kbd.istage, + .ir = kbd.istage, + .ie = kbd.istage + sizeof(kbd.istage), +}; + +char *sysname; +vlong fasthz; + +static void seedrand(void); +static int readtime(ulong, char*, int); +static int readbintime(char*, int); +static int writetime(char*, int); +static int writebintime(char*, int); + +enum +{ + CMhalt, + CMreboot, + CMpanic, +}; + +Cmdtab rebootmsg[] = +{ + CMhalt, "halt", 1, + CMreboot, "reboot", 0, + CMpanic, "panic", 0, +}; + +void +printinit(void) +{ + lineq = qopen(2*1024, 0, nil, nil); + if(lineq == nil) + panic("printinit"); + qnoblock(lineq, 1); +} + +int +consactive(void) +{ + if(serialoq) + return qlen(serialoq) > 0; + return 0; +} + +void +prflush(void) +{ + ulong now; + + now = m->ticks; + while(consactive()) + if(m->ticks - now >= HZ) + break; +} + +/* + * Log console output so it can be retrieved via /dev/kmesg. + * This is good for catching boot-time messages after the fact. + */ +struct { + Lock lk; +// char buf[16384]; /* normal */ + char buf[256*1024]; /* for acpi debugging */ + uint n; +} kmesg; + +static void +kmesgputs(char *str, int n) +{ + uint nn, d; + + ilock(&kmesg.lk); + /* take the tail of huge writes */ + if(n > sizeof kmesg.buf){ + d = n - sizeof kmesg.buf; + str += d; + n -= d; + } + + /* slide the buffer down to make room */ + nn = kmesg.n; + if(nn + n >= sizeof kmesg.buf){ + d = nn + n - sizeof kmesg.buf; + if(d) + memmove(kmesg.buf, kmesg.buf+d, sizeof kmesg.buf-d); + nn -= d; + } + + /* copy the data in */ + memmove(kmesg.buf+nn, str, n); + nn += n; + kmesg.n = nn; + iunlock(&kmesg.lk); +} + +/* + * Print a string on the console. Convert \n to \r\n for serial + * line consoles. Locking of the queues is left up to the screen + * or uart code. Multi-line messages to serial consoles may get + * interspersed with other messages. + */ +static void +putstrn0(char *str, int n, int usewrite) +{ + int m; + char *t; + + if(!islo()) + usewrite = 0; + + /* + * how many different output devices do we need? + */ + kmesgputs(str, n); + + /* + * if someone is reading /dev/kprint, + * put the message there. + * if not and there's an attached bit mapped display, + * put the message there. + * + * if there's a serial line being used as a console, + * put the message there. + */ + if(kprintoq != nil && !qisclosed(kprintoq)){ + if(usewrite) + qwrite(kprintoq, str, n); + else + qiwrite(kprintoq, str, n); + }else if(screenputs != nil) + screenputs(str, n); + + if(serialoq == nil){ + uartputs(str, n); + return; + } + + while(n > 0) { + t = memchr(str, '\n', n); + if(t && !kbd.raw) { + m = t-str; + if(usewrite){ + qwrite(serialoq, str, m); + qwrite(serialoq, "\r\n", 2); + } else { + qiwrite(serialoq, str, m); + qiwrite(serialoq, "\r\n", 2); + } + n -= m+1; + str = t+1; + } else { + if(usewrite) + qwrite(serialoq, str, n); + else + qiwrite(serialoq, str, n); + break; + } + } +} + +void +putstrn(char *str, int n) +{ + putstrn0(str, n, 0); +} + +int noprint; + +int +print(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + if(noprint) + return -1; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + if(!normalprint) { + if(0) iprint("\nprint called too early from %#lux\n", + getcallerpc(&fmt)); + iprint("%.*s", n, buf); + } else + putstrn(buf, n); + + return n; +} + +/* + * Want to interlock iprints to avoid interlaced output on + * multiprocessor, but don't want to deadlock if one processor + * dies during print and another has something important to say. + * Make a good faith effort. + */ +static Lock iprintlock; +static int +iprintcanlock(Lock *l) +{ + int i; + + for(i=0; i<1000; i++){ + if(canlock(l)) + return 1; + if(l->m == MACHP(m->machno)) + return 0; + microdelay(100); + } + return 0; +} + +int +iprint(char *fmt, ...) +{ + int n, s, locked; + va_list arg; + char buf[PRINTSIZE]; + + s = splhi(); + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + locked = iprintcanlock(&iprintlock); + if(screenputs != nil && iprintscreenputs) + screenputs(buf, n); + if(consuart == nil || consuart->phys == nil || + consuart->phys->putc == nil) + _uartputs(buf, n); + else + uartputs(buf, n); + if(locked) + unlock(&iprintlock); + splx(s); + + return n; +} + +void +panic(char *fmt, ...) +{ + int n, s; + va_list arg; + char buf[PRINTSIZE]; + + kprintoq = nil; /* don't try to write to /dev/kprint */ + + if(panicking) + for(;;); + panicking = 1; + + delay(20); + s = splhi(); + strcpy(buf, "panic: "); + va_start(arg, fmt); + n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + iprint("%s\n", buf); + if(consdebug) + (*consdebug)(); + splx(s); + prflush(); + buf[n] = '\n'; +// putstrn(buf, n+1); +// dumpstack(); + + exit(1); +} + +/* libmp at least contains a few calls to sysfatal; simulate with panic */ +void +sysfatal(char *fmt, ...) +{ + char err[256]; + va_list arg; + + va_start(arg, fmt); + vseprint(err, err + sizeof err, fmt, arg); + va_end(arg); + panic("sysfatal: %s", err); +} + +void +_assert(char *fmt) +{ + panic("assert failed at %#p: %s", getcallerpc(&fmt), fmt); +} + +int +pprint(char *fmt, ...) +{ + int n; + Chan *c; + va_list arg; + char buf[2*PRINTSIZE]; + + if(up == nil || up->fgrp == nil) + return 0; + + c = up->fgrp->fd[2]; + if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) + return 0; + n = snprint(buf, sizeof buf, "%s %lud: ", up->text, up->pid); + va_start(arg, fmt); + n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + if(waserror()) + return 0; + devtab[c->type]->write(c, buf, n, c->offset); + poperror(); + + lock(c); + c->offset += n; + unlock(c); + + return n; +} + +static void +echoscreen(char *buf, int n) +{ + char *e, *p; + char ebuf[128]; + int x; + + p = ebuf; + e = ebuf + sizeof(ebuf) - 4; + while(n-- > 0){ + if(p >= e){ + screenputs(ebuf, p - ebuf); + p = ebuf; + } + x = *buf++; + if(x == 0x15){ + *p++ = '^'; + *p++ = 'U'; + *p++ = '\n'; + } else + *p++ = x; + } + if(p != ebuf) + screenputs(ebuf, p - ebuf); +} + +static void +echoserialoq(char *buf, int n) +{ + int x; + char *e, *p; + char ebuf[128]; + + p = ebuf; + e = ebuf + sizeof(ebuf) - 4; + while(n-- > 0){ + if(p >= e){ + qiwrite(serialoq, ebuf, p - ebuf); + p = ebuf; + } + x = *buf++; + if(x == '\n'){ + *p++ = '\r'; + *p++ = '\n'; + } else if(x == 0x15){ + *p++ = '^'; + *p++ = 'U'; + *p++ = '\n'; + } else + *p++ = x; + } + if(p != ebuf) + qiwrite(serialoq, ebuf, p - ebuf); +} + +static void +echo(char *buf, int n) +{ + static int ctrlt, pid; + int x; + char *e, *p; + + if(n == 0) + return; + + e = buf+n; + for(p = buf; p < e; p++){ + switch(*p){ + case 0x10: /* ^P */ + if(cpuserver && !kbd.ctlpoff){ + active.exiting = 1; + return; + } + break; + case 0x14: /* ^T */ + ctrlt++; + if(ctrlt > 2) + ctrlt = 2; + continue; + } + + if(ctrlt != 2) + continue; + + /* ^T escapes */ + ctrlt = 0; + switch(*p){ + case 'S': + x = splhi(); + dumpstack(); + procdump(); + splx(x); + return; + case 's': + dumpstack(); + return; + case 'x': + xsummary(); + ixsummary(); + mallocsummary(); + // memorysummary(); + pagersummary(); + return; + case 'd': + if(consdebug == nil) + consdebug = rdb; + else + consdebug = nil; + print("consdebug now %#p\n", consdebug); + return; + case 'D': + if(consdebug == nil) + consdebug = rdb; + consdebug(); + return; + case 'p': + x = spllo(); + procdump(); + splx(x); + return; + case 'q': + scheddump(); + return; + case 'k': + killbig("^t ^t k"); + return; + case 'r': + exit(0); + return; + } + } + + qproduce(kbdq, buf, n); + if(kbd.raw) + return; + kmesgputs(buf, n); + if(screenputs != nil) + echoscreen(buf, n); + if(serialoq) + echoserialoq(buf, n); +} + +/* + * Called by a uart interrupt for console input. + * + * turn '\r' into '\n' before putting it into the queue. + */ +int +kbdcr2nl(Queue*, int ch) +{ + char *next; + + ilock(&kbd.lockputc); /* just a mutex */ + if(ch == '\r' && !kbd.raw) + ch = '\n'; + next = kbd.iw+1; + if(next >= kbd.ie) + next = kbd.istage; + if(next != kbd.ir){ + *kbd.iw = ch; + kbd.iw = next; + } + iunlock(&kbd.lockputc); + return 0; +} + +/* + * Put character, possibly a rune, into read queue at interrupt time. + * Called at interrupt time to process a character. + */ +int +kbdputc(Queue*, int ch) +{ + int i, n; + char buf[3]; + Rune r; + char *next; + + if(kbd.ir == nil) + return 0; /* in case we're not inited yet */ + + ilock(&kbd.lockputc); /* just a mutex */ + r = ch; + n = runetochar(buf, &r); + for(i = 0; i < n; i++){ + next = kbd.iw+1; + if(next >= kbd.ie) + next = kbd.istage; + if(next == kbd.ir) + break; + *kbd.iw = buf[i]; + kbd.iw = next; + } + iunlock(&kbd.lockputc); + return 0; +} + +/* + * we save up input characters till clock time to reduce + * per character interrupt overhead. + */ +static void +kbdputcclock(void) +{ + char *iw; + + /* this amortizes cost of qproduce */ + if(kbd.iw != kbd.ir){ + iw = kbd.iw; + if(iw < kbd.ir){ + echo(kbd.ir, kbd.ie-kbd.ir); + kbd.ir = kbd.istage; + } + if(kbd.ir != iw){ + echo(kbd.ir, iw-kbd.ir); + kbd.ir = iw; + } + } +} + +enum{ + Qdir, + Qbintime, + Qcons, + Qconsctl, + Qcputime, + Qdrivers, + Qkmesg, + Qkprint, + Qhostdomain, + Qhostowner, + Qnull, + Qosversion, + Qpgrpid, + Qpid, + Qppid, + Qrandom, + Qreboot, + Qswap, + Qsysname, + Qsysstat, + Qtime, + Quser, + Qzero, +}; + +enum +{ + VLNUMSIZE= 22, +}; + +static Dirtab consdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "bintime", {Qbintime}, 24, 0664, + "cons", {Qcons}, 0, 0660, + "consctl", {Qconsctl}, 0, 0220, + "cputime", {Qcputime}, 6*NUMSIZE, 0444, + "drivers", {Qdrivers}, 0, 0444, + "hostdomain", {Qhostdomain}, DOMLEN, 0664, + "hostowner", {Qhostowner}, 0, 0664, + "kmesg", {Qkmesg}, 0, 0440, + "kprint", {Qkprint, 0, QTEXCL}, 0, DMEXCL|0440, + "null", {Qnull}, 0, 0666, + "osversion", {Qosversion}, 0, 0444, + "pgrpid", {Qpgrpid}, NUMSIZE, 0444, + "pid", {Qpid}, NUMSIZE, 0444, + "ppid", {Qppid}, NUMSIZE, 0444, + "random", {Qrandom}, 0, 0444, + "reboot", {Qreboot}, 0, 0664, + "swap", {Qswap}, 0, 0664, + "sysname", {Qsysname}, 0, 0664, + "sysstat", {Qsysstat}, 0, 0666, + "time", {Qtime}, NUMSIZE+3*VLNUMSIZE, 0664, + "user", {Quser}, 0, 0666, + "zero", {Qzero}, 0, 0444, +}; + +int +readnum(ulong off, char *buf, ulong n, ulong val, int size) +{ + char tmp[64]; + + snprint(tmp, sizeof(tmp), "%*lud", size-1, val); + tmp[size-1] = ' '; + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, tmp+off, n); + return n; +} + +int +readstr(ulong off, char *buf, ulong n, char *str) +{ + int size; + + size = strlen(str); + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, str+off, n); + return n; +} + +static void +consinit(void) +{ + todinit(); + randominit(); + /* + * at 115200 baud, the 1024 char buffer takes 56 ms to process, + * processing it every 22 ms should be fine + */ + addclock0link(kbdputcclock, 22); +} + +static Chan* +consattach(char *spec) +{ + return devattach('c', spec); +} + +static Walkqid* +conswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen); +} + +static int +consstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, consdir, nelem(consdir), devgen); +} + +static Chan* +consopen(Chan *c, int omode) +{ + c->aux = nil; + c = devopen(c, omode, consdir, nelem(consdir), devgen); + switch((ulong)c->qid.path){ + case Qconsctl: + incref(&kbd.ctl); + break; + + case Qkprint: + if(tas(&kprintinuse) != 0){ + c->flag &= ~COPEN; + error(Einuse); + } + if(kprintoq == nil){ + kprintoq = qopen(8*1024, Qcoalesce, 0, 0); + if(kprintoq == nil){ + c->flag &= ~COPEN; + error(Enomem); + } + qnoblock(kprintoq, 1); + }else + qreopen(kprintoq); + c->iounit = qiomaxatomic; + break; + } + return c; +} + +static void +consclose(Chan *c) +{ + switch((ulong)c->qid.path){ + /* last close of control file turns off raw */ + case Qconsctl: + if(c->flag&COPEN){ + if(decref(&kbd.ctl) == 0) + kbd.raw = 0; + } + break; + + /* close of kprint allows other opens */ + case Qkprint: + if(c->flag & COPEN){ + kprintinuse = 0; + qhangup(kprintoq, nil); + } + break; + } +} + +static long +consread(Chan *c, void *buf, long n, vlong off) +{ + ulong l; + Mach *mp; + char *b, *bp, ch; + char tmp[256]; /* must be >= 18*NUMSIZE (Qswap) */ + int i, k, id, send; + vlong offset = off; + + if(n <= 0) + return n; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, buf, n, consdir, nelem(consdir), devgen); + + case Qcons: + qlock(&kbd); + if(waserror()) { + qunlock(&kbd); + nexterror(); + } + while(!qcanread(lineq)){ + if(qread(kbdq, &ch, 1) == 0) + continue; + send = 0; + if(ch == 0){ + /* flush output on rawoff -> rawon */ + if(kbd.x > 0) + send = !qcanread(kbdq); + }else if(kbd.raw){ + kbd.line[kbd.x++] = ch; + send = !qcanread(kbdq); + }else{ + switch(ch){ + case '\b': + if(kbd.x > 0) + kbd.x--; + break; + case 0x15: /* ^U */ + kbd.x = 0; + break; + case '\n': + case 0x04: /* ^D */ + send = 1; + default: + if(ch != 0x04) + kbd.line[kbd.x++] = ch; + break; + } + } + if(send || kbd.x == sizeof kbd.line){ + qwrite(lineq, kbd.line, kbd.x); + kbd.x = 0; + } + } + n = qread(lineq, buf, n); + qunlock(&kbd); + poperror(); + return n; + + case Qcputime: + k = offset; + if(k >= 6*NUMSIZE) + return 0; + if(k+n > 6*NUMSIZE) + n = 6*NUMSIZE - k; + /* easiest to format in a separate buffer and copy out */ + for(i=0; i<6 && NUMSIZE*itime[i]; + if(i == TReal) + l = MACHP(0)->ticks - l; + l = TK2MS(l); + readnum(0, tmp+NUMSIZE*i, NUMSIZE, l, NUMSIZE); + } + memmove(buf, tmp+k, n); + return n; + + case Qkmesg: + /* + * This is unlocked to avoid tying up a process + * that's writing to the buffer. kmesg.n never + * gets smaller, so worst case the reader will + * see a slurred buffer. + */ + if(off >= kmesg.n) + n = 0; + else{ + if(off+n > kmesg.n) + n = kmesg.n - off; + memmove(buf, kmesg.buf+off, n); + } + return n; + + case Qkprint: + return qread(kprintoq, buf, n); + + case Qpgrpid: + return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE); + + case Qpid: + return readnum((ulong)offset, buf, n, up->pid, NUMSIZE); + + case Qppid: + return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE); + + case Qtime: + return readtime((ulong)offset, buf, n); + + case Qbintime: + return readbintime(buf, n); + + case Qhostowner: + return readstr((ulong)offset, buf, n, eve); + + case Qhostdomain: + return readstr((ulong)offset, buf, n, hostdomain); + + case Quser: + return readstr((ulong)offset, buf, n, up->user); + + case Qnull: + return 0; + + case Qsysstat: + b = smalloc(conf.nmach*(NUMSIZE*11+1) + 1); /* +1 for NUL */ + bp = b; + for(id = 0; id < 32; id++) { + if(active.machs & (1<cs, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->intr, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->syscall, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->pfault, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->tlbfault, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->tlbpurge, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->load, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, + (mp->perf.avg_inidle*100)/mp->perf.period, + NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, + (mp->perf.avg_inintr*100)/mp->perf.period, + NUMSIZE); + bp += NUMSIZE; + *bp++ = '\n'; + } + } + if(waserror()){ + free(b); + nexterror(); + } + n = readstr((ulong)offset, buf, n, b); + free(b); + poperror(); + return n; + + case Qswap: + snprint(tmp, sizeof tmp, + "%lud memory\n" + "%d pagesize\n" + "%lud kernel\n" + "%lud/%lud user\n" + "%lud/%lud swap\n" + "%lud/%lud kernel malloc\n" + "%lud/%lud kernel draw\n", + conf.npage*BY2PG, + BY2PG, + conf.npage-conf.upages, + palloc.user-palloc.freecount, palloc.user, + conf.nswap-swapalloc.free, conf.nswap, + mainmem->cursize, mainmem->maxsize, + imagmem->cursize, imagmem->maxsize); + + return readstr((ulong)offset, buf, n, tmp); + + case Qsysname: + if(sysname == nil) + return 0; + return readstr((ulong)offset, buf, n, sysname); + + case Qrandom: + return randomread(buf, n); + + case Qdrivers: + b = malloc(READSTR); + if(b == nil) + error(Enomem); + k = 0; + for(i = 0; devtab[i] != nil; i++) + k += snprint(b+k, READSTR-k, "#%C %s\n", + devtab[i]->dc, devtab[i]->name); + if(waserror()){ + free(b); + nexterror(); + } + n = readstr((ulong)offset, buf, n, b); + free(b); + poperror(); + return n; + + case Qzero: + memset(buf, 0, n); + return n; + + case Qosversion: + snprint(tmp, sizeof tmp, "2000"); + n = readstr((ulong)offset, buf, n, tmp); + return n; + + default: + print("consread %#llux\n", c->qid.path); + error(Egreg); + } + return -1; /* never reached */ +} + +static long +conswrite(Chan *c, void *va, long n, vlong off) +{ + char buf[256], ch; + long l, bp; + char *a; + Mach *mp; + int id, fd; + Chan *swc; + ulong offset; + Cmdbuf *cb; + Cmdtab *ct; + + a = va; + offset = off; + + switch((ulong)c->qid.path){ + case Qcons: + /* + * Can't page fault in putstrn, so copy the data locally. + */ + l = n; + while(l > 0){ + bp = l; + if(bp > sizeof buf) + bp = sizeof buf; + memmove(buf, a, bp); + putstrn0(buf, bp, 1); + a += bp; + l -= bp; + } + break; + + case Qconsctl: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + for(a = buf; a;){ + if(strncmp(a, "rawon", 5) == 0){ + kbd.raw = 1; + /* clumsy hack - wake up reader */ + ch = 0; + qwrite(kbdq, &ch, 1); + } else if(strncmp(a, "rawoff", 6) == 0){ + kbd.raw = 0; + } else if(strncmp(a, "ctlpon", 6) == 0){ + kbd.ctlpoff = 0; + } else if(strncmp(a, "ctlpoff", 7) == 0){ + kbd.ctlpoff = 1; + } + if(a = strchr(a, ' ')) + a++; + } + break; + + case Qtime: + if(!iseve()) + error(Eperm); + return writetime(a, n); + + case Qbintime: + if(!iseve()) + error(Eperm); + return writebintime(a, n); + + case Qhostowner: + return hostownerwrite(a, n); + + case Qhostdomain: + return hostdomainwrite(a, n); + + case Quser: + return userwrite(a, n); + + case Qnull: + break; + + case Qreboot: + if(!iseve()) + error(Eperm); + cb = parsecmd(a, n); + + if(waserror()) { + free(cb); + nexterror(); + } + ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg)); + switch(ct->index) { + case CMhalt: + reboot(nil, 0, 0); + break; + case CMreboot: + rebootcmd(cb->nf-1, cb->f+1); + break; + case CMpanic: + *(ulong*)0=0; + panic("/dev/reboot"); + } + poperror(); + free(cb); + break; + + case Qsysstat: + for(id = 0; id < 32; id++) { + if(active.machs & (1<cs = 0; + mp->intr = 0; + mp->syscall = 0; + mp->pfault = 0; + mp->tlbfault = 0; + mp->tlbpurge = 0; + } + } + break; + + case Qswap: + if(n >= sizeof buf) + error(Egreg); + memmove(buf, va, n); /* so we can NUL-terminate */ + buf[n] = 0; + /* start a pager if not already started */ + if(strncmp(buf, "start", 5) == 0){ + kickpager(); + break; + } + if(!iseve()) + error(Eperm); + if(buf[0]<'0' || '9'= sizeof buf) + error(Ebadarg); + strncpy(buf, a, n); + buf[n] = 0; + if(buf[n-1] == '\n') + buf[n-1] = 0; + kstrdup(&sysname, buf); + break; + + default: + print("conswrite: %#llux\n", c->qid.path); + error(Egreg); + } + return n; +} + +Dev consdevtab = { + 'c', + "cons", + + devreset, + consinit, + devshutdown, + consattach, + conswalk, + consstat, + consopen, + devcreate, + consclose, + consread, + devbread, + conswrite, + devbwrite, + devremove, + devwstat, +}; + +static ulong randn; + +static void +seedrand(void) +{ + if(!waserror()){ + randomread((void*)&randn, sizeof(randn)); + poperror(); + } +} + +int +nrand(int n) +{ + if(randn == 0) + seedrand(); + randn = randn*1103515245 + 12345 + MACHP(0)->ticks; + return (randn>>16) % n; +} + +int +rand(void) +{ + nrand(1); + return randn; +} + +static uvlong uvorder = 0x0001020304050607ULL; + +static uchar* +le2vlong(vlong *to, uchar *f) +{ + uchar *t, *o; + int i; + + t = (uchar*)to; + o = (uchar*)&uvorder; + for(i = 0; i < sizeof(vlong); i++) + t[o[i]] = f[i]; + return f+sizeof(vlong); +} + +static uchar* +vlong2le(uchar *t, vlong from) +{ + uchar *f, *o; + int i; + + f = (uchar*)&from; + o = (uchar*)&uvorder; + for(i = 0; i < sizeof(vlong); i++) + t[i] = f[o[i]]; + return t+sizeof(vlong); +} + +static long order = 0x00010203; + +static uchar* +le2long(long *to, uchar *f) +{ + uchar *t, *o; + int i; + + t = (uchar*)to; + o = (uchar*)ℴ + for(i = 0; i < sizeof(long); i++) + t[o[i]] = f[i]; + return f+sizeof(long); +} + +static uchar* +long2le(uchar *t, long from) +{ + uchar *f, *o; + int i; + + f = (uchar*)&from; + o = (uchar*)ℴ + for(i = 0; i < sizeof(long); i++) + t[i] = f[o[i]]; + return t+sizeof(long); +} + +char *Ebadtimectl = "bad time control"; + +/* + * like the old #c/time but with added info. Return + * + * secs nanosecs fastticks fasthz + */ +static int +readtime(ulong off, char *buf, int n) +{ + vlong nsec, ticks; + long sec; + char str[7*NUMSIZE]; + + nsec = todget(&ticks); + if(fasthz == 0LL) + fastticks((uvlong*)&fasthz); + sec = nsec/1000000000ULL; + snprint(str, sizeof(str), "%*lud %*llud %*llud %*llud ", + NUMSIZE-1, sec, + VLNUMSIZE-1, nsec, + VLNUMSIZE-1, ticks, + VLNUMSIZE-1, fasthz); + return readstr(off, buf, n, str); +} + +/* + * set the time in seconds + */ +static int +writetime(char *buf, int n) +{ + char b[13]; + long i; + vlong now; + + if(n >= sizeof(b)) + error(Ebadtimectl); + strncpy(b, buf, n); + b[n] = 0; + i = strtol(b, 0, 0); + if(i <= 0) + error(Ebadtimectl); + now = i*1000000000LL; + todset(now, 0, 0); + return n; +} + +/* + * read binary time info. all numbers are little endian. + * ticks and nsec are syncronized. + */ +static int +readbintime(char *buf, int n) +{ + int i; + vlong nsec, ticks; + uchar *b = (uchar*)buf; + + i = 0; + if(fasthz == 0LL) + fastticks((uvlong*)&fasthz); + nsec = todget(&ticks); + if(n >= 3*sizeof(uvlong)){ + vlong2le(b+2*sizeof(uvlong), fasthz); + i += sizeof(uvlong); + } + if(n >= 2*sizeof(uvlong)){ + vlong2le(b+sizeof(uvlong), ticks); + i += sizeof(uvlong); + } + if(n >= 8){ + vlong2le(b, nsec); + i += sizeof(vlong); + } + return i; +} + +/* + * set any of the following + * - time in nsec + * - nsec trim applied over some seconds + * - clock frequency + */ +static int +writebintime(char *buf, int n) +{ + uchar *p; + vlong delta; + long period; + + n--; + p = (uchar*)buf + 1; + switch(*buf){ + case 'n': + if(n < sizeof(vlong)) + error(Ebadtimectl); + le2vlong(&delta, p); + todset(delta, 0, 0); + break; + case 'd': + if(n < sizeof(vlong)+sizeof(long)) + error(Ebadtimectl); + p = le2vlong(&delta, p); + le2long(&period, p); + todset(-1, delta, period); + break; + case 'f': + if(n < sizeof(uvlong)) + error(Ebadtimectl); + le2vlong(&fasthz, p); + if(fasthz <= 0) + error(Ebadtimectl); + todsetfreq(fasthz); + break; + } + return n; +} diff --git a/sys/src/9/omap/devdss.c b/sys/src/9/omap/devdss.c new file mode 100755 index 000000000..17ad06abd --- /dev/null +++ b/sys/src/9/omap/devdss.c @@ -0,0 +1,180 @@ +/* + * omap35 display subsystem (dss) device interface to screen.c. + * implements #v/vgactl + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" +// #include "gamma.h" + +enum { + Qdir, + Qdss, +}; + +extern OScreen oscreen; +extern Settings settings[]; +extern Omap3fb *framebuf; + +static QLock dsslck; +static Dirtab dsstab[] = { + ".", {Qdir, 0, QTDIR}, 0, 0555|DMDIR, + "vgactl", {Qdss, 0}, 0, 0666, +}; + +static Chan* +screenattach(char *spec) +{ + return devattach('v', spec); +} + +static Walkqid* +screenwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, dsstab, nelem(dsstab), devgen); +} + +static int +screenstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, dsstab, nelem(dsstab), devgen); +} + +static Chan* +screenopen(Chan *c, int omode) +{ + if ((ulong)c->qid.path == Qdss) { + qlock(&dsslck); + oscreen.open = 1; + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + } + return c; +} + +static void +screenclose(Chan *c) +{ + if ((c->qid.type & QTDIR) == 0 && c->flag & COPEN) + if (c->qid.path == Qdss) { + oscreen.open = 0; + qunlock(&dsslck); + } +} + +static ulong +getchans(char *p) +{ + if (strncmp("x24" , p, 3) == 0) + return RGB24; /* can't work yet, pixels are shorts */ + else if (strncmp("x16", p, 3) == 0) + return RGB16; + else + return RGB16; +} + +static long +settingswrite(OScreen *scr, char *p) +{ + if (strncmp("800x600", p, 7) == 0) { + p += 7; + scr->settings = &settings[Res800x600]; + } else if (strncmp("1024x768", p, 8) == 0) { + p += 8; + scr->settings = &settings[Res1024x768]; + } else if (strncmp("1280x1024", p, 9) == 0) { + p += 9; + scr->settings = &settings[Res1280x1024]; + } else + return -1; + scr->settings->chan = getchans(p); + return 1; +} + +static long +screenread(Chan *c, void *a, long n, vlong off) +{ + int len, depth; + char *p; + Settings *set; + + switch ((ulong)c->qid.path) { + case Qdir: + return devdirread(c, a, n, dsstab, nelem(dsstab), devgen); + case Qdss: + set = oscreen.settings; + p = malloc(READSTR); + if(waserror()){ + free(p); + nexterror(); + } + if (set->chan == RGB16) + depth = 16; + else if (set->chan == RGB24) + depth = 24; + else + depth = 0; + len = snprint(p, READSTR, "size %dx%dx%d @ %d Hz\n" + "addr %#p size %ud\n", set->wid, set->ht, depth, + set->freq, framebuf, sizeof *framebuf); + USED(len); + n = readstr(off, a, n, p); + poperror(); + free(p); + return n; + default: + error(Egreg); + } + return 0; +} + +static long +screenwrite(Chan *c, void *a, long n, vlong off) +{ + switch ((ulong)c->qid.path) { + case Qdss: + if(off) + error(Ebadarg); + n = settingswrite(&oscreen, a); + if (n < 0) + error(Ebadctl); + screeninit(); + return n; + default: + error(Egreg); + } + return 0; +} + +Dev dssdevtab = { + L'v', + "dss", + + devreset, + devinit, + devshutdown, // TODO add a shutdown to stop dma to monitor + screenattach, + screenwalk, + screenstat, + screenopen, + devcreate, + screenclose, + screenread, + devbread, + screenwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/sys/src/9/omap/devether.c b/sys/src/9/omap/devether.c new file mode 100755 index 000000000..f6c32b9f0 --- /dev/null +++ b/sys/src/9/omap/devether.c @@ -0,0 +1,530 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/netif.h" +#include "etherif.h" + +extern int archether(unsigned ctlno, Ether *ether); + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + int ctlrno; + char *p; + Chan *chan; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p != 0) + error(Ebadarg); + if(ctlrno < 0 || ctlrno >= MaxEther) + error(Ebadarg); + } + if(etherxx[ctlrno] == 0) + error(Enodev); + + chan = devattach('l', spec); + if(waserror()){ + chanfree(chan); + nexterror(); + } + chan->dev = ctlrno; + if(etherxx[ctlrno]->attach) + etherxx[ctlrno]->attach(etherxx[ctlrno]); + poperror(); + return chan; +} + +static Walkqid* +etherwalk(Chan* chan, Chan* nchan, char** name, int nname) +{ + return netifwalk(etherxx[chan->dev], chan, nchan, name, nname); +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + return netifstat(etherxx[chan->dev], chan, dp, n); +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + return netifopen(etherxx[chan->dev], chan, omode); +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + netifclose(etherxx[chan->dev], chan); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + + ether = etherxx[chan->dev]; + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid) + return ether->ifstat(ether, buf, n, offset); + else if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + + return netifread(ether, chan, buf, n, offset); +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + return netifbread(etherxx[chan->dev], chan, n, offset); +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + return netifwstat(etherxx[chan->dev], chan, dp, n); +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multicast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && + ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if((f = *fp) != nil && (f->type == type || f->type < 0) && + (tome || multi || f->prom)){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) + ether->soverflows++; + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + } else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int nn, onoff; + Cmdbuf *cb; + + ether = etherxx[chan->dev]; + if(NETTYPE(chan->qid.path) != Ndataqid) { + nn = netifwrite(ether, chan, buf, n); + if(nn >= 0) + return nn; + cb = parsecmd(buf, n); + if(cb->f[0] && strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + return n; + } + free(cb); + if(ether->ctl!=nil) + return ether->ctl(ether,buf,n); + + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + poperror(); + bp->wp += n; + + return etheroq(ether, bp); +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + + return etheroq(ether, bp); +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static void +etherreset(void) +{ + Ether *ether; + int i, n, ctlrno; + char name[KNAMELEN], buf[128]; + + for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if(ether == 0) + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + + if(archether(ctlrno, ether) <= 0) + continue; + + if(isaconfig("ether", ctlrno, ether) == 0){ + free(ether); +// return nil; + continue; + } + for(n = 0; cards[n].type; n++){ + if(cistrcmp(cards[n].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++) + if(cistrncmp(ether->opt[i], "ea=", 3) == 0){ + if(parseether(ether->ea, + ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Eaddrlen); + } else if(cistrcmp(ether->opt[i], + "100BASE-TXFD") == 0) + ether->mbps = 100; + if(cards[n].reset(ether)) + break; + snprint(name, sizeof(name), "ether%d", ctlrno); + + if(ether->interrupt != nil && ether->irq >= 0) + intrenable(ether->irq, ether->interrupt, + ether, 0, name); + + i = snprint(buf, sizeof buf, + "#l%d: %s: %dMbps port %#lux irq %d", + ctlrno, ether->type, ether->mbps, ether->port, + ether->irq); + if(ether->mem) + i += snprint(buf+i, sizeof buf - i, + " addr %#lux", PADDR(ether->mem)); + if(ether->size) + i += snprint(buf+i, sizeof buf - i, + " size %#luX", ether->size); + i += snprint(buf+i, sizeof buf - i, + ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + snprint(buf+i, sizeof buf - i, "\n"); + iprint("%s", buf); /* it may be too early for print */ + + if(ether->mbps >= 1000) + netifinit(ether, name, Ntypes, 4*1024*1024); + else if(ether->mbps >= 100) + netifinit(ether, name, Ntypes, 1024*1024); + else + netifinit(ether, name, Ntypes, 65*1024); + if(ether->oq == 0) + ether->oq = qopen(ether->limit, Qmsg, 0, 0); + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + etherxx[ctlrno] = ether; + ether = 0; + break; + } + } + if(ether) + free(ether); +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i = 0; i < MaxEther; i++){ + ether = etherxx[i]; + if(ether == nil) + continue; + if(ether->shutdown == nil) { + print("#l%d: no shutdown function\n", i); + continue; + } + (*ether->shutdown)(ether); + } +} + + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +void +dumpoq(Queue *oq) +{ + if (oq == nil) + print("no outq! "); + else if (qisclosed(oq)) + print("outq closed "); + else if (qfull(oq)) + print("outq full "); + else + print("outq %d ", qlen(oq)); +} + +void +dumpnetif(Netif *netif) +{ + print("netif %s ", netif->name); + print("limit %d mbps %d link %d ", + netif->limit, netif->mbps, netif->link); + print("inpkts %lld outpkts %lld errs %d\n", + netif->inpackets, netif->outpackets, + netif->crcs + netif->oerrs + netif->frames + netif->overflows + + netif->buffs + netif->soverflows); +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, +}; diff --git a/sys/src/9/omap/devuart.c b/sys/src/9/omap/devuart.c new file mode 100755 index 000000000..af4fce768 --- /dev/null +++ b/sys/src/9/omap/devuart.c @@ -0,0 +1,789 @@ +#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" + +enum +{ + /* soft flow control chars */ + CTLS= 023, + CTLQ= 021, +}; + +extern Dev uartdevtab; +extern PhysUart* physuart[]; + +static Uart* uartlist; +static Uart** uart; +static int uartnuart; +static Dirtab *uartdir; +static int uartndir; +static Timer *uarttimer; + +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +static void uartclock(void); +static void uartflow(void*); + +/* + * enable/disable uart and add/remove to list of enabled uarts + */ +//static +Uart* +uartenable(Uart *p) +{ + Uart **l; + + if (up == nil) + return p; /* too soon; try again later */ +// return nil; + + if(p->iq == nil){ + if((p->iq = qopen(8*1024, 0, uartflow, p)) == nil) + return nil; + } + else + qreopen(p->iq); + if(p->oq == nil){ + if((p->oq = qopen(8*1024, 0, uartkick, p)) == nil){ + qfree(p->iq); + p->iq = nil; + return nil; + } + } + else + qreopen(p->oq); + + p->ir = p->istage; + p->iw = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; + + p->hup_dsr = p->hup_dcd = 0; + p->dsr = p->dcd = 0; + + /* assume we can send */ + p->cts = 1; + p->ctsbackoff = 0; + +if (up) { + if(p->bits == 0) + uartctl(p, "l8"); + if(p->stop == 0) + uartctl(p, "s1"); + if(p->parity == 0) + uartctl(p, "pn"); + if(p->baud == 0) + uartctl(p, "b9600"); + (*p->phys->enable)(p, 1); +} + + /* + * use ilock because uartclock can otherwise interrupt here + * and would hang on an attempt to lock uartalloc. + */ + ilock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + iunlock(&uartalloc); + + return p; +} + +static void +uartdisable(Uart *p) +{ + Uart **l; + + (*p->phys->disable)(p); + + ilock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + iunlock(&uartalloc); +} + +void +uartmouse(Uart* p, int (*putc)(Queue*, int), int setb1200) +{ + qlock(p); + if(p->opens++ == 0 && uartenable(p) == nil){ + qunlock(p); + error(Enodev); + } + if(setb1200) + uartctl(p, "b1200"); + p->putc = putc; + p->special = 1; + qunlock(p); +} + +void +uartsetmouseputc(Uart* p, int (*putc)(Queue*, int)) +{ + qlock(p); + if(p->opens == 0 || p->special == 0){ + qunlock(p); + error(Enodev); + } + p->putc = putc; + qunlock(p); +} + +static void +setlength(int i) +{ + Uart *p; + + if(i > 0){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } else for(i = 0; i < uartnuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } +} + +/* + * set up the '#t' directory + */ +static void +uartreset(void) +{ + int i; + Dirtab *dp; + Uart *p, *tail; + + tail = nil; + for(i = 0; physuart[i] != nil; i++){ + if(physuart[i]->pnp == nil) + continue; + if((p = physuart[i]->pnp()) == nil) + continue; + if(uartlist != nil) + tail->next = p; + else + uartlist = p; + for(tail = p; tail->next != nil; tail = tail->next) + uartnuart++; + uartnuart++; + } + + if(uartnuart) + uart = xalloc(uartnuart*sizeof(Uart*)); + + uartndir = 1 + 3*uartnuart; + uartdir = xalloc(uartndir * sizeof(Dirtab)); + if (uart == nil || uartdir == nil) + panic("uartreset: no memory"); + dp = uartdir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + p = uartlist; + for(i = 0; i < uartnuart; i++){ + /* 3 directory entries per port */ + snprint(dp->name, sizeof dp->name, "eia%d", i); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0660; + dp++; + snprint(dp->name, sizeof dp->name, "eia%dctl", i); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0660; + dp++; + snprint(dp->name, sizeof dp->name, "eia%dstatus", i); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + + uart[i] = p; + p->dev = i; + if(p->console || p->special){ + if(uartenable(p) != nil){ + if(p->console && up){ + kbdq = p->iq; + serialoq = p->oq; + p->putc = kbdcr2nl; + } + p->opens++; + } + } + p = p->next; + } + + if(uartnuart){ + /* + * at 115200 baud, the 1024 char buffer takes 56 ms to process, + * processing it every 22 ms should be fine. + */ + uarttimer = addclock0link(uartclock, 22); + } +} + + +static Chan* +uartattach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +uartwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, uartdir, uartndir, devgen); +} + +static int +uartstat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, uartdir, uartndir, devgen); +} + +static Chan* +uartopen(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, uartdir, uartndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0 && uartenable(p) == nil){ + qunlock(p); + c->flag &= ~COPEN; + error(Enodev); + } + qunlock(p); + break; + } + + c->iounit = qiomaxatomic; + return c; +} + +static int +uartdrained(void* arg) +{ + Uart *p; + + p = arg; + return qlen(p->oq) == 0 && p->op == p->oe; +} + +static void +uartdrainoutput(Uart *p) +{ + if(!p->enabled || up == nil) + return; + + p->drain = 1; + if(waserror()){ + p->drain = 0; + nexterror(); + } + sleep(&p->r, uartdrained, p); + poperror(); +} + +static void +uartclose(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + qclose(p->iq); + ilock(&p->rlock); + p->ir = p->iw = p->istage; + iunlock(&p->rlock); + + /* + */ + qhangup(p->oq, nil); + if(!waserror()){ + uartdrainoutput(p); + poperror(); + } + qclose(p->oq); + uartdisable(p); + p->dcd = p->dsr = p->dohup = 0; + } + qunlock(p); + break; + } +} + +static long +uartread(Chan *c, void *buf, long n, vlong off) +{ + Uart *p; + ulong offset = off; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, uartdir, uartndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return (*p->phys->status)(p, buf, n, offset); + } + + return 0; +} + +int +uartctl(Uart *p, char *cmd) +{ + char *f[16]; + int i, n, nf; + + nf = tokenize(cmd, f, nelem(f)); + for(i = 0; i < nf; i++){ + if(strncmp(f[i], "break", 5) == 0){ + (*p->phys->dobreak)(p, 0); + continue; + } + + n = atoi(f[i]+1); + switch(*f[i]){ + case 'B': + case 'b': + uartdrainoutput(p); + if((*p->phys->baud)(p, n) < 0) + return -1; + break; + case 'C': + case 'c': + p->hup_dcd = n; + break; + case 'D': + case 'd': + uartdrainoutput(p); + (*p->phys->dtr)(p, n); + break; + case 'E': + case 'e': + p->hup_dsr = n; + break; + case 'f': + case 'F': + if(p->oq != nil) + qflush(p->oq); + break; + case 'H': + case 'h': + if(p->iq != nil) + qhangup(p->iq, 0); + if(p->oq != nil) + qhangup(p->oq, 0); + break; + case 'i': + case 'I': + uartdrainoutput(p); + (*p->phys->fifo)(p, n); + break; + case 'K': + case 'k': + uartdrainoutput(p); + (*p->phys->dobreak)(p, n); + break; + case 'L': + case 'l': + uartdrainoutput(p); + if((*p->phys->bits)(p, n) < 0) + return -1; + break; + case 'm': + case 'M': + uartdrainoutput(p); + (*p->phys->modemctl)(p, n); + break; + case 'n': + case 'N': + if(p->oq != nil) + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + uartdrainoutput(p); + if((*p->phys->parity)(p, *(f[i]+1)) < 0) + return -1; + break; + case 'Q': + case 'q': + if(p->iq != nil) + qsetlimit(p->iq, n); + if(p->oq != nil) + qsetlimit(p->oq, n); + break; + case 'R': + case 'r': + uartdrainoutput(p); + (*p->phys->rts)(p, n); + break; + case 'S': + case 's': + uartdrainoutput(p); + if((*p->phys->stop)(p, n) < 0) + return -1; + break; + case 'W': + case 'w': + if(uarttimer == nil || n < 1) + return -1; + uarttimer->tns = (vlong)n * 100000LL; + break; + case 'X': + case 'x': + if(p->enabled){ + ilock(&p->tlock); + p->xonoff = n; + iunlock(&p->tlock); + } + break; + } + } + return 0; +} + +static long +uartwrite(Chan *c, void *buf, long n, vlong) +{ + Uart *p; + char *cmd; + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + qlock(p); + if(waserror()){ + qunlock(p); + nexterror(); + } + + n = qwrite(p->oq, buf, n); + + qunlock(p); + poperror(); + break; + case Nctlqid: + cmd = malloc(n+1); + memmove(cmd, buf, n); + cmd[n] = 0; + qlock(p); + if(waserror()){ + qunlock(p); + free(cmd); + nexterror(); + } + + /* let output drain */ + if(uartctl(p, cmd) < 0) + error(Ebadarg); + + qunlock(p); + poperror(); + free(cmd); + break; + } + + return n; +} + +static int +uartwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(QTDIR & c->qid.type) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &uartdir[1 + 3 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + if(d.mode != ~0UL) + dt[0].perm = dt[1].perm = d.mode; + return n; +} + +void +uartpower(int on) +{ + Uart *p; + + for(p = uartlist; p != nil; p = p->next) { + if(p->phys->power) + (*p->phys->power)(p, on); + } +} + +Dev uartdevtab = { + 't', + "uart", + + uartreset, + devinit, + devshutdown, + uartattach, + uartwalk, + uartstat, + uartopen, + devcreate, + uartclose, + uartread, + devbread, + uartwrite, + devbwrite, + devremove, + uartwstat, + uartpower, +}; + +/* + * restart input if it's off + */ +static void +uartflow(void *v) +{ + Uart *p; + + p = v; + if(p->modem) + (*p->phys->rts)(p, 1); +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +int +uartstageoutput(Uart *p) +{ + int n; + + n = qconsume(p->oq, p->ostage, Stagesize); + if(n <= 0) +// n = 0; /* experiment */ + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +/* + * restart output + */ +void +uartkick(void *v) +{ + Uart *p = v; + + if(p->blocked) + return; + + ilock(&p->tlock); + (*p->phys->kick)(p); + iunlock(&p->tlock); + + if(p->drain && uartdrained(p)){ + p->drain = 0; + wakeup(&p->r); + } +} + +/* + * Move data from the interrupt staging area to + * the input Queue. + */ +static void +uartstageinput(Uart *p) +{ + int n; + uchar *ir, *iw; + + while(p->ir != p->iw){ + ir = p->ir; + if(p->ir > p->iw){ + iw = p->ie; + p->ir = p->istage; + } + else{ + iw = p->iw; + p->ir = p->iw; + } + if((n = qproduce(p->iq, ir, iw - ir)) < 0){ + p->serr++; + (*p->phys->rts)(p, 0); + } + else if(n == 0) + p->berr++; + } +} + +/* + * receive a character at interrupt time + */ +void +uartrecv(Uart *p, char ch) +{ + uchar *next; + + /* software flow control */ + if(p->xonoff){ + if(ch == CTLS){ + p->blocked = 1; + }else if(ch == CTLQ){ + p->blocked = 0; + p->ctsbackoff = 2; /* clock gets output going again */ + } + } + + /* receive the character */ + if(p->putc) + p->putc(p->iq, ch); + else if (p->iw) { /* maybe the line isn't enabled yet */ + ilock(&p->rlock); + next = p->iw + 1; + if(next == p->ie) + next = p->istage; + if(next == p->ir) + uartstageinput(p); + if(next != p->ir){ + *p->iw = ch; + p->iw = next; + } + iunlock(&p->rlock); + } +} + +/* + * we save up input characters till clock time to reduce + * per character interrupt overhead. + */ +static void +uartclock(void) +{ + Uart *p; + + ilock(&uartalloc); + for(p = uartalloc.elist; p; p = p->elist){ + + /* this hopefully amortizes cost of qproduce to many chars */ + if(p->iw != p->ir){ + ilock(&p->rlock); + uartstageinput(p); + iunlock(&p->rlock); + } + + /* hang up if requested */ + if(p->dohup){ + qhangup(p->iq, 0); + qhangup(p->oq, 0); + p->dohup = 0; + } + + /* this adds hysteresis to hardware/software flow control */ + if(p->ctsbackoff){ + ilock(&p->tlock); + if(p->ctsbackoff){ + if(--(p->ctsbackoff) == 0) + (*p->phys->kick)(p); + } + iunlock(&p->tlock); + } + uartkick(p); /* keep it moving */ + } + iunlock(&uartalloc); +} + +/* + * polling console input, output + */ + +Uart* consuart; + +int +uartgetc(void) +{ + if(consuart == nil || consuart->phys->getc == nil) + return -1; + return consuart->phys->getc(consuart); +} + +void +uartputc(int c) +{ + if(consuart == nil || consuart->phys->putc == nil) + return; + consuart->phys->putc(consuart, c); +} + +void +uartputs(char *s, int n) +{ + char *e; + + if(consuart == nil || consuart->phys->putc == nil) + return; + + e = s+n; + for(; sphys->putc(consuart, '\r'); + consuart->phys->putc(consuart, *s); + } +} diff --git a/sys/src/9/omap/devusb.c b/sys/src/9/omap/devusb.c new file mode 100755 index 000000000..efe93a931 --- /dev/null +++ b/sys/src/9/omap/devusb.c @@ -0,0 +1,1461 @@ +/* + * USB device driver framework. + * + * This is in charge of providing access to actual HCIs + * and providing I/O to the various endpoints of devices. + * A separate user program (usbd) is in charge of + * enumerating the bus, setting up endpoints and + * starting devices (also user programs). + * + * The interface provided is a violation of the standard: + * you're welcome. + * + * The interface consists of a root directory with several files + * plus a directory (epN.M) with two files per endpoint. + * A device is represented by its first endpoint, which + * is a control endpoint automatically allocated for each device. + * Device control endpoints may be used to create new endpoints. + * Devices corresponding to hubs may also allocate new devices, + * perhaps also hubs. Initially, a hub device is allocated for + * each controller present, to represent its root hub. Those can + * never be removed. + * + * All endpoints refer to the first endpoint (epN.0) of the device, + * which keeps per-device information, and also to the HCI used + * to reach them. Although all endpoints cache that information. + * + * epN.M/data files permit I/O and are considered DMEXCL. + * epN.M/ctl files provide status info and accept control requests. + * + * Endpoints may be given file names to be listed also at #u, + * for those drivers that have nothing to do after configuring the + * device and its endpoints. + * + * Drivers for different controllers are kept at usb[oue]hci.c + * It's likely we could factor out much from controllers into + * a generic controller driver, the problem is that details + * regarding how to handle toggles, tokens, Tds, etc. will + * get in the way. Thus, code is probably easier the way it is. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/usb.h" + +typedef struct Hcitype Hcitype; + +enum +{ + /* Qid numbers */ + Qdir = 0, /* #u */ + Qusbdir, /* #u/usb */ + Qctl, /* #u/usb/ctl - control requests */ + + Qep0dir, /* #u/usb/ep0.0 - endpoint 0 dir */ + Qep0io, /* #u/usb/ep0.0/data - endpoint 0 I/O */ + Qep0ctl, /* #u/usb/ep0.0/ctl - endpoint 0 ctl. */ + Qep0dummy, /* give 4 qids to each endpoint */ + + Qepdir = 0, /* (qid-qep0dir)&3 is one of these */ + Qepio, /* to identify which file for the endpoint */ + Qepctl, + + /* ... */ + + /* Usb ctls. */ + CMdebug = 0, /* debug on|off */ + CMdump, /* dump (data structures for debug) */ + + /* Ep. ctls */ + CMnew = 0, /* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */ + CMnewdev, /* newdev full|low|high portnb (allocate new devices) */ + CMhub, /* hub (set the device as a hub) */ + CMspeed, /* speed full|low|high|no */ + CMmaxpkt, /* maxpkt size */ + CMntds, /* ntds nb (max nb. of tds per µframe) */ + CMclrhalt, /* clrhalt (halt was cleared on endpoint) */ + CMpollival, /* pollival interval (interrupt/iso) */ + CMhz, /* hz n (samples/sec; iso) */ + CMsamplesz, /* samplesz n (sample size; iso) */ + CMinfo, /* info infostr (ke.ep info for humans) */ + CMdetach, /* detach (abort I/O forever on this ep). */ + CMaddress, /* address (address is assigned) */ + CMdebugep, /* debug n (set/clear debug for this ep) */ + CMname, /* name str (show up as #u/name as well) */ + CMtmout, /* timeout n (activate timeouts for ep) */ + CMpreset, /* reset the port */ + + /* Hub feature selectors */ + Rportenable = 1, + Rportreset = 4, + +}; + +struct Hcitype +{ + char* type; + int (*reset)(Hci*); +}; + +#define QID(q) ((int)(q).path) + +static char Edetach[] = "device is detached"; +static char Enotconf[] = "endpoint not configured"; +char Estalled[] = "endpoint stalled"; + +static Cmdtab usbctls[] = +{ + {CMdebug, "debug", 2}, + {CMdump, "dump", 1}, +}; + +static Cmdtab epctls[] = +{ + {CMnew, "new", 4}, + {CMnewdev, "newdev", 3}, + {CMhub, "hub", 1}, + {CMspeed, "speed", 2}, + {CMmaxpkt, "maxpkt", 2}, + {CMntds, "ntds", 2}, + {CMpollival, "pollival", 2}, + {CMsamplesz, "samplesz", 2}, + {CMhz, "hz", 2}, + {CMinfo, "info", 0}, + {CMdetach, "detach", 1}, + {CMaddress, "address", 1}, + {CMdebugep, "debug", 2}, + {CMclrhalt, "clrhalt", 1}, + {CMname, "name", 2}, + {CMtmout, "timeout", 2}, + {CMpreset, "reset", 1}, +}; + +static Dirtab usbdir[] = +{ + "ctl", {Qctl}, 0, 0666, +}; + +char *usbmodename[] = +{ + [OREAD] "r", + [OWRITE] "w", + [ORDWR] "rw", +}; + +static char *ttname[] = +{ + [Tnone] "none", + [Tctl] "control", + [Tiso] "iso", + [Tintr] "interrupt", + [Tbulk] "bulk", +}; + +static char *spname[] = +{ + [Fullspeed] "full", + [Lowspeed] "low", + [Highspeed] "high", + [Nospeed] "no", +}; + +static int debug; +static Hcitype hcitypes[Nhcis]; +static Hci* hcis[Nhcis]; +static QLock epslck; /* add, del, lookup endpoints */ +static Ep* eps[Neps]; /* all endpoints known */ +static int epmax; /* 1 + last endpoint index used */ +static int usbidgen; /* device address generator */ + +/* + * Is there something like this in a library? should it be? + */ +char* +seprintdata(char *s, char *se, uchar *d, int n) +{ + int i, l; + + s = seprint(s, se, " %#p[%d]: ", d, n); + l = n; + if(l > 10) + l = 10; + for(i=0; i= epmax || eps[q] == nil) + return -1; + return q; +} + +static int +isqtype(int q, int type) +{ + if(q < Qep0dir) + return 0; + q -= Qep0dir; + return (q & 3) == type; +} + +void +addhcitype(char* t, int (*r)(Hci*)) +{ + static int ntype; + + if(ntype == Nhcis) + panic("too many USB host interface types"); + hcitypes[ntype].type = t; + hcitypes[ntype].reset = r; + ntype++; +} + +static char* +seprintep(char *s, char *se, Ep *ep, int all) +{ + static char* dsnames[] = { "config", "enabled", "detached", "reset" }; + Udev *d; + int i; + int di; + + d = ep->dev; + + qlock(ep); + if(waserror()){ + qunlock(ep); + nexterror(); + } + di = ep->dev->nb; + if(all) + s = seprint(s, se, "dev %d ep %d ", di, ep->nb); + s = seprint(s, se, "%s", dsnames[ep->dev->state]); + s = seprint(s, se, " %s", ttname[ep->ttype]); + assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR); + s = seprint(s, se, " %s", usbmodename[ep->mode]); + s = seprint(s, se, " speed %s", spname[d->speed]); + s = seprint(s, se, " maxpkt %ld", ep->maxpkt); + s = seprint(s, se, " pollival %ld", ep->pollival); + s = seprint(s, se, " samplesz %ld", ep->samplesz); + 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); + if(ep->inuse) + s = seprint(s, se, " busy"); + else + s = seprint(s, se, " idle"); + if(all){ + s = seprint(s, se, " load %uld", ep->load); + s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep); + s = seprint(s, se, " idx %d", ep->idx); + if(ep->name != nil) + s = seprint(s, se, " name '%s'", ep->name); + if(ep->tmout != 0) + s = seprint(s, se, " tmout"); + if(ep == ep->ep0){ + s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno); + s = seprint(s, se, " eps:"); + for(i = 0; i < nelem(d->eps); i++) + if(d->eps[i] != nil) + s = seprint(s, se, " ep%d.%d", di, i); + } + } + if(ep->info != nil) + s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type); + else + s = seprint(s, se, "\n"); + qunlock(ep); + poperror(); + return s; +} + +static Ep* +epalloc(Hci *hp) +{ + Ep *ep; + int i; + + ep = smalloc(sizeof(Ep)); + ep->ref = 1; + qlock(&epslck); + for(i = 0; i < Neps; i++) + if(eps[i] == nil) + break; + if(i == Neps){ + qunlock(&epslck); + free(ep); + print("usb: bug: too few endpoints.\n"); + return nil; + } + ep->idx = i; + if(epmax <= i) + epmax = i+1; + eps[i] = ep; + ep->hp = hp; + ep->maxpkt = 8; + ep->ntds = 1; + ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */ + qunlock(&epslck); + return ep; +} + +static Ep* +getep(int i) +{ + Ep *ep; + + if(i < 0 || i >= epmax || eps[i] == nil) + return nil; + qlock(&epslck); + ep = eps[i]; + if(ep != nil) + incref(ep); + qunlock(&epslck); + return ep; +} + +static void +putep(Ep *ep) +{ + Udev *d; + + if(ep != nil && decref(ep) == 0){ + d = ep->dev; + deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep); + qlock(&epslck); + eps[ep->idx] = nil; + if(ep->idx == epmax-1) + epmax--; + if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen) + usbidgen--; + qunlock(&epslck); + if(d != nil){ + qlock(ep->ep0); + d->eps[ep->nb] = nil; + qunlock(ep->ep0); + } + if(ep->ep0 != ep){ + putep(ep->ep0); + ep->ep0 = nil; + } + free(ep->info); + free(ep->name); + free(ep); + } +} + +static void +dumpeps(void) +{ + int i; + static char buf[512]; + char *s; + char *e; + Ep *ep; + + print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps); + for(i = 0; i < epmax; i++){ + s = buf; + e = buf+sizeof(buf); + ep = getep(i); + if(ep != nil){ + if(waserror()){ + putep(ep); + nexterror(); + } + s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb); + seprintep(s, e, ep, 1); + print("%s", buf); + ep->hp->seprintep(buf, e, ep); + print("%s", buf); + poperror(); + putep(ep); + } + } + print("usb dump hcis:\n"); + for(i = 0; i < Nhcis; i++) + if(hcis[i] != nil) + hcis[i]->dump(hcis[i]); +} + +static int +newusbid(Hci *) +{ + int id; + + qlock(&epslck); + id = ++usbidgen; + if(id >= 0x7F) + print("#u: too many device addresses; reuse them more\n"); + qunlock(&epslck); + return id; +} + +/* + * Create endpoint 0 for a new device + */ +static Ep* +newdev(Hci *hp, int ishub, int isroot) +{ + Ep *ep; + Udev *d; + + ep = epalloc(hp); + d = ep->dev = smalloc(sizeof(Udev)); + d->nb = newusbid(hp); + 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->state = Dconfig; /* address not yet set */ + ep->dev = d; + ep->ep0 = ep; /* no ref counted here */ + ep->ttype = Tctl; + ep->tmout = Xfertmout; + ep->mode = ORDWR; + dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep); + return ep; +} + +/* + * Create a new endpoint for the device + * accessed via the given endpoint 0. + */ +static Ep* +newdevep(Ep *ep, int i, int tt, int mode) +{ + Ep *nep; + Udev *d; + + d = ep->dev; + if(d->eps[i] != nil) + error("endpoint already in use"); + nep = epalloc(ep->hp); + incref(ep); + d->eps[i] = nep; + nep->nb = i; + nep->toggle[0] = nep->toggle[1] = 0; + nep->ep0 = ep; + nep->dev = ep->dev; + nep->mode = mode; + nep->ttype = tt; + nep->debug = ep->debug; + /* set defaults */ + switch(tt){ + case Tctl: + nep->tmout = Xfertmout; + break; + case Tintr: + nep->pollival = 10; + break; + case Tiso: + nep->tmout = Xfertmout; + nep->pollival = 10; + nep->samplesz = 4; + nep->hz = 44100; + break; + } + deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep); + return ep; +} + +static int +epdataperm(int mode) +{ + + switch(mode){ + case OREAD: + return 0440|DMEXCL; + break; + case OWRITE: + return 0220|DMEXCL; + break; + default: + return 0660|DMEXCL; + } +} + +static int +usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + Dirtab *dir; + int perm; + char *se; + Ep *ep; + int nb; + int mode; + + if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s); + if(s == DEVDOTDOT){ + if(QID(c->qid) <= Qusbdir){ + mkqid(&q, Qdir, 0, QTDIR); + devdir(c, q, "#u", 0, eve, 0555, dp); + }else{ + mkqid(&q, Qusbdir, 0, QTDIR); + devdir(c, q, "usb", 0, eve, 0555, dp); + } + if(0)ddprint("ok\n"); + return 1; + } + + switch(QID(c->qid)){ + case Qdir: /* list #u */ + if(s == 0){ + mkqid(&q, Qusbdir, 0, QTDIR); + devdir(c, q, "usb", 0, eve, 0555, dp); + if(0)ddprint("ok\n"); + return 1; + } + s--; + if(s < 0 || s >= epmax) + goto Fail; + ep = getep(s); + if(ep == nil || ep->name == nil){ + if(ep != nil) + putep(ep); + if(0)ddprint("skip\n"); + return 0; + } + if(waserror()){ + putep(ep); + nexterror(); + } + mkqid(&q, Qep0io+s*4, 0, QTFILE); + devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp); + putep(ep); + poperror(); + if(0)ddprint("ok\n"); + return 1; + + case Qusbdir: /* list #u/usb */ + Usbdir: + if(s < nelem(usbdir)){ + dir = &usbdir[s]; + mkqid(&q, dir->qid.path, 0, QTFILE); + devdir(c, q, dir->name, dir->length, eve, dir->perm, dp); + if(0)ddprint("ok\n"); + return 1; + } + s -= nelem(usbdir); + if(s < 0 || s >= epmax) + goto Fail; + ep = getep(s); + if(ep == nil){ + if(0)ddprint("skip\n"); + return 0; + } + if(waserror()){ + putep(ep); + nexterror(); + } + se = up->genbuf+sizeof(up->genbuf); + seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb); + mkqid(&q, Qep0dir+4*s, 0, QTDIR); + putep(ep); + poperror(); + devdir(c, q, up->genbuf, 0, eve, 0755, dp); + if(0)ddprint("ok\n"); + return 1; + + case Qctl: + s = 0; + goto Usbdir; + + default: /* list #u/usb/epN.M */ + nb = qid2epidx(QID(c->qid)); + ep = getep(nb); + if(ep == nil) + goto Fail; + mode = ep->mode; + putep(ep); + if(isqtype(QID(c->qid), Qepdir)){ + Epdir: + switch(s){ + case 0: + mkqid(&q, Qep0io+nb*4, 0, QTFILE); + perm = epdataperm(mode); + devdir(c, q, "data", 0, eve, perm, dp); + break; + case 1: + mkqid(&q, Qep0ctl+nb*4, 0, QTFILE); + devdir(c, q, "ctl", 0, eve, 0664, dp); + break; + default: + goto Fail; + } + }else if(isqtype(QID(c->qid), Qepctl)){ + s = 1; + goto Epdir; + }else{ + s = 0; + goto Epdir; + } + if(0)ddprint("ok\n"); + return 1; + } +Fail: + if(0)ddprint("fail\n"); + return -1; +} + +static Hci* +hciprobe(int cardno, int ctlrno) +{ + Hci *hp; + char *type; + char name[64]; + static int epnb = 1; /* guess the endpoint nb. for the controller */ + + ddprint("hciprobe %d %d\n", cardno, ctlrno); + hp = smalloc(sizeof(Hci)); + hp->ctlrno = ctlrno; + + if(cardno < 0) + for(cardno = 0; cardno < Nhcis; cardno++){ + if(hcitypes[cardno].type == nil) + break; + type = hp->type; + if(type==nil || *type==0) + type = "uhci"; + if(cistrcmp(hcitypes[cardno].type, type) == 0) + break; + } + + if(cardno >= Nhcis || hcitypes[cardno].type == nil){ + free(hp); + return nil; + } + dprint("%s...", hcitypes[cardno].type); + if(hcitypes[cardno].reset(hp) < 0){ + free(hp); + return nil; + } + + snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type); + intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, name); + + print("#u/usb/ep%d.0: %s: port %#luX irq %d\n", + epnb, hcitypes[cardno].type, hp->port, hp->irq); + epnb++; + + return hp; +} + +static void +usbreset(void) +{ + int cardno, ctlrno; + Hci *hp; + + dprint("usbreset\n"); + + for(ctlrno = 0; ctlrno < Nhcis; ctlrno++) + if((hp = hciprobe(-1, ctlrno)) != nil) + hcis[ctlrno] = hp; + cardno = ctlrno = 0; + while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil) + if(hcis[ctlrno] != nil) + ctlrno++; + else{ + hp = hciprobe(cardno, ctlrno); + if(hp == nil) + cardno++; + hcis[ctlrno++] = hp; + } + if(hcis[Nhcis-1] != nil) + print("usbreset: bug: Nhcis too small\n"); +} + +static void +usbinit(void) +{ + Hci *hp; + int ctlrno; + Ep *d; + char info[40]; + + dprint("usbinit\n"); + for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){ + hp = hcis[ctlrno]; + if(hp != nil){ + if(hp->init != nil) + 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); + } + } +} + +static Chan* +usbattach(char *spec) +{ + return devattach(L'u', spec); +} + +static Walkqid* +usbwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, usbgen); +} + +static int +usbstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, usbgen); +} + +/* + * µs for the given transfer, for bandwidth allocation. + * This is a very rough worst case for what 5.11.3 + * of the usb 2.0 spec says. + * Also, we are using maxpkt and not actual transfer sizes. + * Only when we are sure we + * are not exceeding b/w might we consider adjusting it. + */ +static ulong +usbload(int speed, int maxpkt) +{ + enum{ Hostns = 1000, Hubns = 333 }; + ulong l; + ulong bs; + + l = 0; + bs = 10UL * maxpkt; + switch(speed){ + case Highspeed: + l = 55*8*2 + 2 * (3 + bs) + Hostns; + break; + case Fullspeed: + l = 9107 + 84 * (4 + bs) + Hostns; + break; + case Lowspeed: + l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns; + break; + default: + print("usbload: bad speed %d\n", speed); + /* let it run */ + } + return l / 1000UL; /* in µs */ +} + +static Chan* +usbopen(Chan *c, int omode) +{ + int q; + Ep *ep; + int mode; + + mode = openmode(omode); + q = QID(c->qid); + + if(q >= Qep0dir && qid2epidx(q) < 0) + error(Eio); + if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir)) + return devopen(c, omode, nil, 0, usbgen); + + ep = getep(qid2epidx(q)); + if(ep == nil) + error(Eio); + deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode); + if(waserror()){ + putep(ep); + nexterror(); + } + qlock(ep); + if(ep->inuse){ + qunlock(ep); + error(Einuse); + } + ep->inuse = 1; + qunlock(ep); + if(waserror()){ + ep->inuse = 0; + nexterror(); + } + if(mode != OREAD && ep->mode == OREAD) + error(Eperm); + if(mode != OWRITE && ep->mode == OWRITE) + error(Eperm); + if(ep->ttype == Tnone) + error(Enotconf); + ep->clrhalt = 0; + ep->rhrepl = -1; + if(ep->load == 0) + ep->load = usbload(ep->dev->speed, ep->maxpkt); + ep->hp->epopen(ep); + + poperror(); /* ep->inuse */ + poperror(); /* don't putep(): ref kept for fid using the ep. */ + + c->mode = mode; + c->flag |= COPEN; + c->offset = 0; + c->aux = nil; /* paranoia */ + return c; +} + +static void +epclose(Ep *ep) +{ + qlock(ep); + if(waserror()){ + qunlock(ep); + nexterror(); + } + if(ep->inuse){ + ep->hp->epclose(ep); + ep->inuse = 0; + } + qunlock(ep); + poperror(); +} + +static void +usbclose(Chan *c) +{ + int q; + Ep *ep; + + q = QID(c->qid); + if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir)) + return; + + ep = getep(qid2epidx(q)); + if(ep == nil) + return; + deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref); + if(waserror()){ + putep(ep); + nexterror(); + } + if(c->flag & COPEN){ + free(c->aux); + c->aux = nil; + epclose(ep); + putep(ep); /* release ref kept since usbopen */ + c->flag &= ~COPEN; + } + poperror(); + putep(ep); +} + +static long +ctlread(Chan *c, void *a, long n, vlong offset) +{ + int q; + char *s; + char *us; + char *se; + Ep *ep; + int i; + + q = QID(c->qid); + us = s = smalloc(READSTR); + se = s + READSTR; + if(waserror()){ + free(us); + nexterror(); + } + if(q == Qctl) + for(i = 0; i < epmax; i++){ + ep = getep(i); + if(ep != nil){ + if(waserror()){ + putep(ep); + nexterror(); + } + s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb); + s = seprintep(s, se, ep, 0); + poperror(); + } + putep(ep); + } + else{ + ep = getep(qid2epidx(q)); + if(ep == nil) + error(Eio); + if(waserror()){ + putep(ep); + nexterror(); + } + if(c->aux != nil){ + /* After a new endpoint request we read + * the new endpoint name back. + */ + strecpy(s, se, c->aux); + free(c->aux); + c->aux = nil; + }else + seprintep(s, se, ep, 0); + poperror(); + putep(ep); + } + n = readstr(offset, a, n, us); + poperror(); + free(us); + return n; +} + +/* + * Fake root hub emulation. + */ +static long +rhubread(Ep *ep, void *a, long n) +{ + char *b; + + if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2) + return -1; + if(ep->rhrepl < 0) + return -1; + + b = a; + memset(b, 0, n); + PUT2(b, ep->rhrepl); + ep->rhrepl = -1; + return n; +} + +static long +rhubwrite(Ep *ep, void *a, long n) +{ + uchar *s; + int cmd; + int feature; + int port; + Hci *hp; + + if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0) + return -1; + if(n != Rsetuplen) + error("root hub is a toy hub"); + ep->rhrepl = -1; + s = a; + if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother)) + error("root hub is a toy hub"); + hp = ep->hp; + cmd = s[Rreq]; + feature = GET2(s+Rvalue); + port = GET2(s+Rindex); + if(port < 1 || port > hp->nports) + error("bad hub port number"); + switch(feature){ + case Rportenable: + ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature); + break; + case Rportreset: + ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature); + break; + case Rgetstatus: + ep->rhrepl = hp->portstatus(hp, port); + break; + default: + ep->rhrepl = 0; + } + return n; +} + +static long +usbread(Chan *c, void *a, long n, vlong offset) +{ + int q; + Ep *ep; + int nr; + + q = QID(c->qid); + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, nil, 0, usbgen); + + if(q == Qctl || isqtype(q, Qepctl)) + return ctlread(c, a, n, offset); + + ep = getep(qid2epidx(q)); + if(ep == nil) + error(Eio); + if(waserror()){ + putep(ep); + nexterror(); + } + if(ep->dev->state == Ddetach) + error(Edetach); + if(ep->mode == OWRITE || ep->inuse == 0) + error(Ebadusefd); + switch(ep->ttype){ + case Tnone: + error("endpoint not configured"); + case Tctl: + nr = rhubread(ep, a, n); + if(nr >= 0){ + n = nr; + break; + } + /* else fall */ + default: + ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset); + n = ep->hp->epread(ep, a, n); + break; + } + poperror(); + putep(ep); + return n; +} + +static long +pow2(int n) +{ + return 1 << n; +} + +static void +setmaxpkt(Ep *ep, char* s) +{ + long spp; /* samples per packet */ + + if(ep->dev->speed == Highspeed) + spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000; + else + spp = (ep->hz * ep->pollival + 999) / 1000; + 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; + } +} + +/* + * Many endpoint ctls. simply update the portable representation + * of the endpoint. The actual controller driver will look + * at them to setup the endpoints as dictated. + */ +static long +epctl(Ep *ep, Chan *c, void *a, long n) +{ + int i, l, mode, nb, tt; + char *b, *s; + Cmdbuf *cb; + Cmdtab *ct; + Ep *nep; + Udev *d; + static char *Info = "info "; + + d = ep->dev; + + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, epctls, nelem(epctls)); + if(ct == nil) + error(Ebadctl); + i = ct->index; + if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset) + if(ep != ep->ep0) + error("allowed only on a setup endpoint"); + if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname) + if(ep != ep->ep0 && ep->inuse != 0) + error("must configure before using"); + switch(i){ + case CMnew: + deprint("usb epctl %s\n", cb->f[0]); + nb = strtol(cb->f[1], nil, 0); + if(nb < 0 || nb >= Ndeveps) + error("bad endpoint number"); + tt = name2ttype(cb->f[2]); + if(tt == Tnone) + error("unknown endpoint type"); + mode = name2mode(cb->f[3]); + if(mode < 0) + error("unknown i/o mode"); + newdevep(ep, nb, tt, mode); + break; + case CMnewdev: + deprint("usb epctl %s\n", cb->f[0]); + if(ep != ep->ep0 || d->ishub == 0) + error("not a hub setup endpoint"); + l = name2speed(cb->f[1]); + if(l == Nospeed) + error("speed must be full|low|high"); + nep = newdev(ep->hp, 0, 0); + nep->dev->speed = l; + if(nep->dev->speed != Lowspeed) + nep->maxpkt = 64; /* assume full speed */ + nep->dev->hub = d->nb; + nep->dev->port = atoi(cb->f[2]); + /* next read request will read + * the name for the new endpoint + */ + l = sizeof(up->genbuf); + snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb); + kstrdup(&c->aux, up->genbuf); + break; + case CMhub: + deprint("usb epctl %s\n", cb->f[0]); + d->ishub = 1; + break; + case CMspeed: + 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"); + qlock(ep->ep0); + d->speed = l; + qunlock(ep->ep0); + break; + case CMmaxpkt: + l = strtoul(cb->f[1], nil, 0); + deprint("usb epctl %s %d\n", cb->f[0], l); + if(l < 1 || l > 1024) + error("maxpkt not in [1:1024]"); + qlock(ep); + ep->maxpkt = l; + qunlock(ep); + break; + case CMntds: + l = strtoul(cb->f[1], nil, 0); + deprint("usb epctl %s %d\n", cb->f[0], l); + if(l < 1 || l > 3) + error("ntds not in [1:3]"); + qlock(ep); + ep->ntds = l; + qunlock(ep); + break; + case CMpollival: + if(ep->ttype != Tintr && ep->ttype != Tiso) + error("not an intr or iso endpoint"); + l = strtoul(cb->f[1], nil, 0); + deprint("usb epctl %s %d\n", cb->f[0], l); + if(ep->ttype == Tiso || + (ep->ttype == Tintr && ep->dev->speed == Highspeed)){ + if(l < 1 || l > 16) + error("pollival power not in [1:16]"); + l = pow2(l-1); + }else + if(l < 1 || l > 255) + error("pollival not in [1:255]"); + qlock(ep); + ep->pollival = l; + if(ep->ttype == Tiso) + setmaxpkt(ep, "pollival"); + qunlock(ep); + break; + case CMsamplesz: + if(ep->ttype != Tiso) + error("not an iso endpoint"); + l = strtoul(cb->f[1], nil, 0); + deprint("usb epctl %s %d\n", cb->f[0], l); + if(l <= 0 || l > 8) + error("samplesz not in [1:8]"); + qlock(ep); + ep->samplesz = l; + setmaxpkt(ep, "samplesz"); + qunlock(ep); + break; + case CMhz: + if(ep->ttype != Tiso) + error("not an iso endpoint"); + l = strtoul(cb->f[1], nil, 0); + deprint("usb epctl %s %d\n", cb->f[0], l); + if(l <= 0 || l > 100000) + error("hz not in [1:100000]"); + qlock(ep); + ep->hz = l; + setmaxpkt(ep, "hz"); + qunlock(ep); + break; + case CMclrhalt: + qlock(ep); + deprint("usb epctl %s\n", cb->f[0]); + ep->clrhalt = 1; + qunlock(ep); + break; + case CMinfo: + deprint("usb epctl %s\n", cb->f[0]); + l = strlen(Info); + s = a; + if(n < l+2 || strncmp(Info, s, l) != 0) + error(Ebadctl); + if(n > 1024) + n = 1024; + b = smalloc(n); + memmove(b, s+l, n-l); + b[n-l] = 0; + if(b[n-l-1] == '\n') + b[n-l-1] = 0; + qlock(ep); + free(ep->info); + ep->info = b; + qunlock(ep); + break; + case CMaddress: + deprint("usb epctl %s\n", cb->f[0]); + ep->dev->state = Denabled; + break; + case CMdetach: + if(ep->dev->isroot != 0) + error("can't detach a root hub"); + deprint("usb epctl %s ep%d.%d\n", + cb->f[0], ep->dev->nb, ep->nb); + ep->dev->state = Ddetach; + /* Release file system ref. for its endpoints */ + for(i = 0; i < nelem(ep->dev->eps); i++) + putep(ep->dev->eps[i]); + break; + case CMdebugep: + if(strcmp(cb->f[1], "on") == 0) + ep->debug = 1; + else if(strcmp(cb->f[1], "off") == 0) + ep->debug = 0; + else + ep->debug = strtoul(cb->f[1], nil, 0); + print("usb: ep%d.%d debug %d\n", + ep->dev->nb, ep->nb, ep->debug); + break; + case CMname: + deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]); + validname(cb->f[1], 0); + kstrdup(&ep->name, cb->f[1]); + break; + case CMtmout: + deprint("usb epctl %s\n", cb->f[0]); + if(ep->ttype == Tiso || ep->ttype == Tctl) + error("ctl ignored for this endpoint type"); + ep->tmout = strtoul(cb->f[1], nil, 0); + if(ep->tmout != 0 && ep->tmout < Xfertmout) + ep->tmout = Xfertmout; + break; + case CMpreset: + deprint("usb epctl %s\n", cb->f[0]); + if(ep->ttype != Tctl) + error("not a control endpoint"); + if(ep->dev->state != Denabled) + error("forbidden on devices not enabled"); + ep->dev->state = Dreset; + break; + default: + panic("usb: unknown epctl %d", ct->index); + } + free(cb); + poperror(); + return n; +} + +static long +usbctl(void *a, long n) +{ + Cmdtab *ct; + Cmdbuf *cb; + Ep *ep; + int i; + + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, usbctls, nelem(usbctls)); + dprint("usb ctl %s\n", cb->f[0]); + switch(ct->index){ + case CMdebug: + if(strcmp(cb->f[1], "on") == 0) + debug = 1; + else if(strcmp(cb->f[1], "off") == 0) + debug = 0; + else + debug = strtol(cb->f[1], nil, 0); + print("usb: debug %d\n", debug); + for(i = 0; i < epmax; i++) + if((ep = getep(i)) != nil){ + ep->hp->debug(ep->hp, debug); + putep(ep); + } + break; + case CMdump: + dumpeps(); + break; + } + free(cb); + poperror(); + return n; +} + +static long +ctlwrite(Chan *c, void *a, long n) +{ + int q; + Ep *ep; + + q = QID(c->qid); + if(q == Qctl) + return usbctl(a, n); + + ep = getep(qid2epidx(q)); + if(ep == nil) + error(Eio); + if(waserror()){ + putep(ep); + nexterror(); + } + if(ep->dev->state == Ddetach) + error(Edetach); + if(isqtype(q, Qepctl) && c->aux != nil){ + /* Be sure we don't keep a cloned ep name */ + free(c->aux); + c->aux = nil; + error("read, not write, expected"); + } + n = epctl(ep, c, a, n); + putep(ep); + poperror(); + return n; +} + +static long +usbwrite(Chan *c, void *a, long n, vlong off) +{ + int nr, q; + Ep *ep; + + if(c->qid.type == QTDIR) + error(Eisdir); + + q = QID(c->qid); + + if(q == Qctl || isqtype(q, Qepctl)) + return ctlwrite(c, a, n); + + ep = getep(qid2epidx(q)); + if(ep == nil) + error(Eio); + if(waserror()){ + putep(ep); + nexterror(); + } + if(ep->dev->state == Ddetach) + error(Edetach); + if(ep->mode == OREAD || ep->inuse == 0) + error(Ebadusefd); + + switch(ep->ttype){ + case Tnone: + error("endpoint not configured"); + case Tctl: + nr = rhubwrite(ep, a, n); + if(nr >= 0){ + n = nr; + break; + } + /* else fall */ + default: + ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off); + ep->hp->epwrite(ep, a, n); + } + putep(ep); + poperror(); + return n; +} + +void +usbshutdown(void) +{ + Hci *hp; + int i; + + for(i = 0; i < Nhcis; i++){ + hp = hcis[i]; + if(hp == nil) + continue; + if(hp->shutdown == nil) + print("#u: no shutdown function for %s\n", hp->type); + else + hp->shutdown(hp); + } +} + +Dev usbdevtab = { + L'u', + "usb", + + usbreset, + usbinit, + usbshutdown, + usbattach, + usbwalk, + usbstat, + usbopen, + devcreate, + usbclose, + usbread, + devbread, + usbwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/sys/src/9/omap/dma.c b/sys/src/9/omap/dma.c new file mode 100755 index 000000000..385fcd45d --- /dev/null +++ b/sys/src/9/omap/dma.c @@ -0,0 +1,263 @@ +/* + * omap3530 system dma controller + * + * terminology: a block consist of frame(s), a frame consist of elements + * (uchar, ushort, or ulong sized). + */ +#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" + +enum { + Nirq = 4, + Baseirq = 12, + + Nchan = 32, +}; + +/* + * has a sw reset bit + * dma req lines 1, 2, 6, 63 are available for `system expansion' + */ + +typedef struct Regs Regs; +typedef struct Dchan Dchan; +struct Regs { + uchar _pad0[8]; + /* bitfield of intrs pending, by Dchan; write 1s to clear */ + ulong irqsts[Nirq]; + ulong irqen[Nirq]; /* bitfield of intrs enabled, by Dchan */ + ulong syssts; /* 1<<0 is Resetdone */ + ulong syscfg; /* 1<<1 is Softreset */ + uchar _pad1[0x64 - 0x30]; + + ulong caps[5]; /* caps[1] not defined */ + ulong gcr; /* knobs */ + ulong _pad2; + + struct Dchan { + ulong ccr; /* chan ctrl: incr, etc. */ + ulong clnkctrl; /* link ctrl */ + ulong cicr; /* intr ctrl */ + ulong csr; /* status */ + ulong csdp; /* src & dest params */ + ulong cen; /* element # */ + ulong cfn; /* frame # */ + ulong cssa; /* src start addr */ + ulong cdsa; /* dest start addr */ + ulong csei; /* src element index */ + ulong csfi; /* src frame index | pkt size */ + ulong cdei; /* dest element index */ + ulong cdfi; /* dest frame index | pkt size */ + ulong csac; /* src addr value (read-only?) */ + ulong cdac; /* dest addr value */ + ulong ccen; /* curr transferred element # (in frame) */ + ulong ccfn; /* curr transferred frame # (in xfer) */ + ulong color; + uchar _pad3[24]; + } chan[Nchan]; +}; + +enum { + /* cicr/csr bits */ + Blocki = 1 << 5, + + /* ccr bits */ + Enable = 1 << 7, +}; + +typedef struct Xfer Xfer; +static struct Xfer { + Rendez *rend; + int *done; /* flag to set on intr */ +} xfer[Nirq]; + +int +isdmadone(int irq) +{ + Dchan *cp; + Regs *regs = (Regs *)PHYSSDMA; + + cp = regs->chan + irq; + return cp->csr & Blocki; +} + +static void +dmaintr(Ureg *, void *a) +{ + int i = (int)a; /* dma request & chan # */ + Dchan *cp; + Regs *regs = (Regs *)PHYSSDMA; + + assert(i >= 0 && i < Nirq); + + *xfer[i].done = 1; + assert(xfer[i].rend != nil); + wakeup(xfer[i].rend); + + cp = regs->chan + i; + if(!(cp->csr & Blocki)) + iprint("dmaintr: req %d: Blocki not set; csr %#lux\n", + i, cp->csr); + cp->csr |= cp->csr; /* extinguish intr source */ + coherence(); + regs->irqsts[i] = regs->irqsts[i]; /* extinguish intr source */ + coherence(); + regs->irqen[i] &= ~(1 << i); + coherence(); + + xfer[i].rend = nil; + coherence(); +} + +void +zerowds(ulong *wdp, int cnt) +{ + while (cnt-- > 0) + *wdp++ = 0; +} + +static int +istestdmadone(void *arg) +{ + return *(int *)arg; +} + +void +dmainit(void) +{ + int n; + char name[16]; + Dchan *cp; + Regs *regs = (Regs *)PHYSSDMA; + + if (probeaddr((uintptr)®s->syssts) < 0) + panic("dmainit: no syssts reg"); + regs->syssts = 0; + coherence(); + regs->syscfg |= 1<<1; /* Softreset */ + coherence(); + while(!(regs->syssts & (1<<0))) /* Resetdone? */ + ; + + for (n = 0; n < Nchan; n++) { + cp = regs->chan + n; + cp->ccr = 0; + cp->clnkctrl = 0; + cp->cicr = 0; + cp->csr = 0; + cp->csdp = 0; + cp->cen = cp->cfn = 0; + cp->cssa = cp->cdsa = 0; + cp->csei = cp->csfi = 0; + cp->cdei = cp->cdfi = 0; +// cp->csac = cp->cdac = 0; // ro + cp->ccen = cp->ccfn = 0; + cp->color = 0; + } + zerowds((void *)regs->irqsts, sizeof regs->irqsts / sizeof(ulong)); + zerowds((void *)regs->irqen, sizeof regs->irqen / sizeof(ulong)); + coherence(); + + regs->gcr = 65; /* burst size + 1 */ + coherence(); + + for (n = 0; n < Nirq; n++) { + snprint(name, sizeof name, "dma%d", n); + intrenable(Baseirq + n, dmaintr, (void *)n, nil, name); + } +} + +enum { + Testbyte = 0252, + Testsize = 256, + Scratch = MB, +}; + +/* + * try to confirm sane operation + */ +void +dmatest(void) +{ + int n, done; + uchar *bp; + static ulong pat = 0x87654321; + static Rendez trendez; + + if (up == nil) + panic("dmatest: up not set yet"); + bp = (uchar *)KADDR(PHYSDRAM + 128*MB); + memset(bp, Testbyte, Scratch); + done = 0; + dmastart((void *)PADDR(bp), Postincr, (void *)PADDR(&pat), Const, + Testsize, &trendez, &done); + sleep(&trendez, istestdmadone, &done); + cachedinvse(bp, Scratch); + + if (((ulong *)bp)[0] != pat) + panic("dmainit: copied incorrect data %#lux != %#lux", + ((ulong *)bp)[0], pat); + for (n = Testsize; n < Scratch && bp[n] != Testbyte; n++) + ; + if (n >= Scratch) + panic("dmainit: ran wild over memory, clobbered ≥%,d bytes", n); + if (bp[n] == Testbyte && n != Testsize) + iprint("dma: %d-byte dma stopped after %d bytes!\n", + Testsize, n); +} + +/* addresses are physical */ +int +dmastart(void *to, int tmode, void *from, int fmode, uint len, Rendez *rend, + int *done) +{ + int irq, chan; + uint ruplen; + Dchan *cp; + Regs *regs = (Regs *)PHYSSDMA; + static Lock alloclck; + + /* allocate free irq (and chan) */ + ilock(&alloclck); + for (irq = 0; irq < Nirq && xfer[irq].rend != nil; irq++) + ; + if (irq >= Nirq) + panic("dmastart: no available irqs; too many concurrent dmas"); + chan = irq; + xfer[irq].rend = rend; /* for wakeup at intr time */ + xfer[irq].done = done; + *done = 0; + iunlock(&alloclck); + + ruplen = ROUNDUP(len, sizeof(ulong)); + assert(to != from); + + cp = regs->chan + chan; + cp->ccr &= ~Enable; /* paranoia */ + cp->cicr = 0; + regs->irqen[irq] &= ~(1 << chan); + coherence(); + + cp->csdp = 2; /* 2 = log2(sizeof(ulong)) */ + cp->cssa = (uintptr)from; + cp->cdsa = (uintptr)to; + cp->ccr = tmode << 14 | fmode << 12; + cp->csei = cp->csfi = cp->cdei = cp->cdfi = 1; + cp->cen = ruplen / sizeof(ulong); /* ulongs / frame */ + cp->cfn = 1; /* 1 frame / xfer */ + cp->cicr = Blocki; /* intr at end of block */ + + regs->irqen[irq] |= 1 << chan; + coherence(); + + cp->ccr |= Enable; /* fire! */ + coherence(); + + return irq; +} diff --git a/sys/src/9/omap/ether9221.c b/sys/src/9/omap/ether9221.c new file mode 100755 index 000000000..9f7a571aa --- /dev/null +++ b/sys/src/9/omap/ether9221.c @@ -0,0 +1,961 @@ +/* + * SMSC 9221 Ethernet driver + * specifically for the ISEE IGEPv2 board, + * where it is assigned to Chip Select 5, + * its registers are at 0x2c000000 (inherited from u-boot), + * and irq is 34 from gpio pin 176, thus gpio module 6. + * + * it's slow due to the use of fifos instead of buffer rings. + * the slow system dma just makes it worse. + * + * igepv2 u-boot uses pin 64 on gpio 3 as an output pin to reset the 9221. + */ +#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" + +/* currently using kprocs is a lot slower than not (87 s. to boot vs 60) */ +#undef USE_KPROCS + +enum { + Vid9221 = 0x9221, + Slop = 4, /* beyond ETHERMAXTU */ +}; + +typedef struct Regs Regs; +struct Regs { + /* fifo ports */ + ulong rxdata; + uchar _pad0[0x20 - 4]; + ulong txdata; + uchar _pad1[0x40 - 0x24]; + ulong rxsts; + ulong rxstspeek; + ulong txsts; + ulong txstspeek; + + /* control & status */ + ushort rev; /* chip revision */ + ushort id; /* chip id, 0x9221 */ + ulong irqcfg; + ulong intsts; + ulong inten; + ulong _pad2; + ulong bytetest; + ulong fifoint; /* fifo level interrupts */ + ulong rxcfg; + ulong txcfg; + ulong hwcfg; + ulong rxdpctl; /* rx data path control */ + ulong rxfifoinf; + ulong txfifoinf; + ulong pmtctl; /* power mgmt. control */ + ulong gpiocfg; + ulong gptcfg; /* timer */ + ulong gptcnt; + ulong _pad3; + ulong wordswap; + ulong freerun; /* counters */ + ulong rxdrop; + + /* + * mac registers are accessed indirectly via the mac csr registers. + * phy registers are doubly indirect, via the mac csr mii_acc & + * mii_data mac csr registers. + */ + ulong maccsrcmd; /* mac csr synchronizer */ + ulong maccsrdata; + ulong afccfg; /* automatic flow control cfg. */ + ulong eepcmd; /* eeprom */ + ulong eepdata; + /* 0xb8 */ +}; + +enum { + Nstatistics = 128, +}; + +enum { + /* txcmda bits */ + Intcompl = 1<<31, + Bufendalign = 3<<24, /* mask */ + Datastoff = 037<<16, /* mask */ + Firstseg = 1<<13, + Lastseg = 1<<12, + Bufsize = MASK(11), + + /* txcmdb bits */ + Pkttag = MASK(16) << 16, + Txcksumen = 1<<14, + Addcrcdis = 1<<13, + Framepaddis = 1<<12, + Pktlen = (1<<1) - 1, /* mask */ + + /* txcfg bits */ + Txsdump = 1<<15, /* flush tx status fifo */ + Txddump = 1<<14, /* flush tx data fifo */ + Txon = 1<<1, + Stoptx = 1<<0, + + /* hwcfg bits */ + Mbo = 1<<20, /* must be one */ + Srstto = 1<<1, /* soft reset time-out */ + Srst = 1<<0, + + /* rxcfg bits */ + Rxdmacntshift = 16, /* ulong count, 12 bits wide */ + Rxdmacntmask = MASK(12) << Rxdmacntshift, + Rxdump = 1<<15, /* flush rx fifos */ + + /* rxsts bits */ + Rxpktlenshift = 16, /* byte count */ + Rxpktlenmask = MASK(14) << Rxpktlenshift, + Rxerr = 1<<15, + + /* rxfifoinf bits */ + Rxstsusedshift = 16, /* ulong count */ + Rxstsusedmask = MASK(8) << Rxstsusedshift, + Rxdatausedmask = MASK(16), /* byte count */ + + /* txfifoinf bits */ + Txstsusedshift = 16, /* ulong count */ + Txstsusedmask = MASK(8) << Txstsusedshift, + Txdatafreemask = MASK(16), /* byte count */ + + /* pmtctl bits */ + Dready = 1<<0, + + /* maccsrcmd bits */ + Csrbusy = 1<<31, + Csrread = 1<<30, /* not write */ + Csraddrshift = 0, + Csraddrmask = MASK(8) - 1, + + /* mac registers' indices */ + Maccr = 1, + Macaddrh, + Macaddrl, + Machashh, + Machashl, + Macmiiacc, /* for doubly-indirect phy access */ + Macmiidata, + Macflow, + Macvlan1, + Macvlan2, + Macwuff, + Macwucsr, + Maccoe, + + /* Maccr bits */ + Rxall = 1<<31, + Rcvown = 1<<23, /* don't receive own transmissions */ + Fdpx = 1<<20, /* full duplex */ + Mcpas = 1<<19, /* pass all multicast */ + Prms = 1<<18, /* promiscuous */ + Ho = 1<<15, /* hash-only filtering */ + Hpfilt = 1<<13, /* hash/perfect filtering */ + Padstr = 1<<8, /* strip padding & fcs (crc) */ + Txen = 1<<3, + Rxen = 1<<2, + + /* irqcfg bits */ + Irqdeasclr = 1<<14, /* deassertion intv'l clear */ + Irqdeassts = 1<<13, /* deassertion intv'l status */ + Irqint = 1<<12, /* intr being asserted? (ro) */ + Irqen = 1<<8, + Irqpol = 1<<4, /* irq output is active high */ + Irqpushpull = 1<<0, /* irq output is push/pull driver */ + + /* intsts/inten bits */ + Swint = 1<<31, /* generate an interrupt */ + Txstop = 1<<25, + Rxstop = 1<<24, + Txioc = 1<<21, + Rxdma = 1<<20, + Gptimer = 1<<19, + Phy = 1<<18, + Rxe = 1<<14, /* errors */ + Txe = 1<<13, + Tdfo = 1<<10, /* tx data fifo overrun */ + Tdfa = 1<<9, /* tx data fifo available */ + Tsff = 1<<8, /* tx status fifo full */ + Tsfl = 1<<7, /* tx status fifo level */ + Rsff = 1<<4, /* rx status fifo full */ + Rsfl = 1<<3, /* rx status fifo level */ + + /* eepcmd bits */ + Epcbusy = 1<<31, + Epccmdshift = 28, /* interesting one is Reload (7) */ + Epctimeout = 1<<9, + Epcmacloaded = 1<<8, + Epcaddrshift = 0, +}; + +enum { + Rxintrs = Rsff | Rsfl | Rxe, + Txintrs = Tsff | Tsfl | Txe | Txioc, +}; + +/* wake-up frame filter */ +struct Wakeup { + ulong bytemask[4]; /* index is filter # */ + uchar filt0cmd; /* filter 0 command */ + uchar _pad0; + uchar filt1cmd; + uchar _pad1; + uchar filt2cmd; + uchar _pad2; + uchar filt3cmd; + uchar _pad3; + uchar offset[4]; /* index is filter # */ + ushort crc16[4]; /* " */ +}; + +typedef struct Ctlr Ctlr; +struct Ctlr { + int port; + Ctlr* next; + Ether* edev; + Regs* regs; + int active; + int started; + int inited; + int id; + int cls; + ushort eeprom[0x40]; + + QLock alock; /* attach */ + int nrb; /* how many this Ctlr has in the pool */ + + int* nic; + Lock imlock; + int im; /* interrupt mask */ + +// Mii* mii; +// Rendez lrendez; + int lim; + + int link; + + QLock slock; + uint statistics[Nstatistics]; + uint lsleep; + uint lintr; + uint rsleep; + uint rintr; + int tsleep; + uint tintr; + + uchar ra[Eaddrlen]; /* receive address */ + ulong mta[128]; /* multicast table array */ + + Rendez rrendez; + int gotinput; + int rdcpydone; + + Rendez trendez; + int gotoutput; + int wrcpydone; + + Lock tlock; +}; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static Ctlr *smcctlrhead, *smcctlrtail; + +static char* statistics[Nstatistics] = { "dummy", }; + +static uchar mymac[] = { 0xb0, 0x0f, 0xba, 0xbe, 0x00, 0x00, }; + +static void etherclock(void); +static void smcreceive(Ether *edev); +static void smcinterrupt(Ureg*, void* arg); + +static Ether *thisether; +static int attached; + +static void +smconce(Ether *edev) +{ + static int beenhere; + static Lock l; + + ilock(&l); + if (!beenhere && edev != nil) { + beenhere = 1; + /* simulate interrupts if we don't know the irq */ + if (edev->irq < 0) { /* poll as backup */ + thisether = edev; + addclock0link(etherclock, 1000/HZ); + iprint(" polling"); + } + } + iunlock(&l); +} + +/* + * indirect (mac) register access + */ + +static void +macwait(Regs *regs) +{ + long bound; + + for (bound = 400*Mhz; regs->maccsrcmd & Csrbusy && bound > 0; bound--) + ; + if (bound <= 0) + iprint("smc: mac registers didn't come ready\n"); +} + +static ulong +macrd(Regs *regs, uchar index) +{ + macwait(regs); + regs->maccsrcmd = Csrbusy | Csrread | index; + coherence(); /* back-to-back write/read delay per §6.2.1 */ + macwait(regs); + return regs->maccsrdata; +} + +static void +macwr(Regs *regs, uchar index, ulong val) +{ + macwait(regs); + regs->maccsrdata = val; + regs->maccsrcmd = Csrbusy | index; /* fire */ + macwait(regs); +} + + +static long +smcifstat(Ether* edev, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *p, *s; + int i, l, r; + + ctlr = edev->ctlr; + qlock(&ctlr->slock); + p = malloc(READSTR); + l = 0; + for(i = 0; i < Nstatistics; i++){ + // read regs->rxdrop TODO + r = 0; + if((s = statistics[i]) == nil) + continue; + switch(i){ + default: + ctlr->statistics[i] += r; + if(ctlr->statistics[i] == 0) + continue; + l += snprint(p+l, READSTR-l, "%s: %ud %ud\n", + s, ctlr->statistics[i], r); + break; + } + } + + l += snprint(p+l, READSTR-l, "lintr: %ud %ud\n", + ctlr->lintr, ctlr->lsleep); + l += snprint(p+l, READSTR-l, "rintr: %ud %ud\n", + ctlr->rintr, ctlr->rsleep); + l += snprint(p+l, READSTR-l, "tintr: %ud %ud\n", + ctlr->tintr, ctlr->tsleep); + + l += snprint(p+l, READSTR-l, "eeprom:"); + for(i = 0; i < 0x40; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]); + } + l += snprint(p+l, READSTR-l, "\n"); + USED(l); + + n = readstr(offset, a, n, p); + free(p); + qunlock(&ctlr->slock); + + return n; +} + +static void +smcpromiscuous(void* arg, int on) +{ + int rctl; + Ctlr *ctlr; + Ether *edev; + Regs *regs; + + edev = arg; + ctlr = edev->ctlr; + regs = ctlr->regs; + rctl = macrd(regs, Maccr); + if(on) + rctl |= Prms; + else + rctl &= ~Prms; + macwr(regs, Maccr, rctl); +} + +static void +smcmulticast(void*, uchar*, int) +{ + /* nothing to do, we allow all multicast packets in */ +} + +static int +iswrcpydone(void *arg) +{ + return ((Ctlr *)arg)->wrcpydone; +} + +static int +smctxstart(Ctlr *ctlr, uchar *ubuf, uint len) +{ + uint wds, ruplen; + ulong *wdp, *txdp; + Regs *regs; + static ulong buf[ROUNDUP(ETHERMAXTU, sizeof(ulong)) / sizeof(ulong)]; + + if (!ctlr->inited) { + iprint("smctxstart: too soon to send\n"); + return -1; /* toss it */ + } + regs = ctlr->regs; + + /* is there room for a packet in the tx data fifo? */ + if (len < ETHERMINTU) + iprint("sending too-short (%d) pkt\n", len); + else if (len > ETHERMAXTU) + iprint("sending jumbo (%d) pkt\n", len); + + ruplen = ROUNDUP(len, sizeof(ulong)); + coherence(); /* back-to-back read/read delay per §6.2.2 */ + if ((regs->txfifoinf & Txdatafreemask) < ruplen + 2*sizeof(ulong)) + return -1; /* not enough room for data + command words */ + + if ((uintptr)ubuf & MASK(2)) { /* ensure word alignment */ + memmove(buf, ubuf, len); + ubuf = (uchar *)buf; + } + + /* tx cmd a: length is bytes in this buffer */ + txdp = ®s->txdata; + *txdp = Intcompl | Firstseg | Lastseg | len; + /* tx cmd b: length is bytes in this packet (could be multiple buf.s) */ + *txdp = len; + + /* shovel pkt into tx fifo, which triggers transmission due to Txon */ + wdp = (ulong *)ubuf; + for (wds = ruplen / sizeof(ulong) + 1; --wds > 0; ) + *txdp = *wdp++; + + regs->intsts = Txintrs; /* dismiss intr */ + coherence(); + regs->inten |= Txintrs; + coherence(); /* back-to-back write/read delay per §6.2.1 */ + return 0; +} + +static void +smctransmit(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + if (ctlr == nil) + panic("smctransmit: nil ctlr"); + ilock(&ctlr->tlock); + /* + * Try to fill the chip's buffers back up, via the tx fifo. + */ + while ((bp = qget(edev->oq)) != nil) + if (smctxstart(ctlr, bp->rp, BLEN(bp)) < 0) { + qputback(edev->oq, bp); /* retry the block later */ + iprint("smctransmit: tx data fifo full\n"); + break; + } else + freeb(bp); + iunlock(&ctlr->tlock); +} + +static void +smctransmitcall(Ether *edev) /* called from devether.c */ +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + ctlr->gotoutput = 1; +#ifdef USE_KPROCS + wakeup(&ctlr->trendez); +#else + smctransmit(edev); +#endif +} + +static int +smcrim(void* ctlr) +{ + return ((Ctlr*)ctlr)->gotinput; +} + +static void +smcrproc(void* arg) +{ + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + for(;;){ + ctlr->rsleep++; + sleep(&ctlr->rrendez, smcrim, ctlr); + + /* process any newly-arrived packets and pass to etheriq */ + ctlr->gotinput = 0; + smcreceive(edev); + } +} + +static int +smcgotout(void* ctlr) +{ + return ((Ctlr*)ctlr)->gotoutput; +} + +static void +smctproc(void* arg) +{ + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + for(;;){ + ctlr->tsleep++; + sleep(&ctlr->trendez, smcgotout, ctlr); + + /* process any newly-arrived packets and pass to etheriq */ + ctlr->gotoutput = 0; + smctransmit(edev); + } +} + +void gpioirqclr(void); + +static void +smcattach(Ether* edev) +{ +#ifdef USE_KPROCS + char name[KNAMELEN]; +#endif + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(waserror()){ + qunlock(&ctlr->alock); + nexterror(); + } + if (!ctlr->inited) { + ctlr->inited = 1; +#ifdef USE_KPROCS + snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno); + kproc(name, smcrproc, edev); + + snprint(name, KNAMELEN, "#l%dtproc", edev->ctlrno); + kproc(name, smctproc, edev); +#endif + +iprint("smcattach:"); +#ifdef USE_KPROCS +iprint(" with kprocs"); +#else +iprint(" no kprocs"); +#endif +iprint(", no dma"); + /* can now accept real or simulated interrupts */ + + smconce(edev); + attached = 1; +iprint("\n"); + } + qunlock(&ctlr->alock); + poperror(); +} + +static int +isrdcpydone(void *arg) +{ + return ((Ctlr *)arg)->rdcpydone; +} + +static void +smcreceive(Ether *edev) +{ + uint wds, len, sts; + ulong *wdp, *rxdp; + Block *bp; + Ctlr *ctlr; + Regs *regs; + + ctlr = edev->ctlr; + regs = ctlr->regs; + coherence(); /* back-to-back read/read delay per §6.2.2 */ + /* + * is there a full packet in the rx data fifo? + */ + while (((regs->rxfifoinf & Rxstsusedmask) >> Rxstsusedshift) != 0) { + coherence(); + sts = regs->rxsts; /* pop rx status */ + if(sts & Rxerr) + iprint("smcreceive: rx error\n"); + len = (sts & Rxpktlenmask) >> Rxpktlenshift; + if (len > ETHERMAXTU + Slop) + iprint("smcreceive: oversized rx pkt (%d)\n", len); + else if (len < ETHERMINTU) + iprint("smcreceive: too-short (%d) pkt\n", len); + wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong); + if (wds > 0) { + /* copy aligned words from rx fifo into a Block */ + bp = iallocb(len + sizeof(ulong) /* - 1 */); + if (bp == nil) + panic("smcreceive: nil Block*"); + + /* bp->rp should be 32-byte aligned, more than we need */ + assert(((uintptr)bp->rp & (sizeof(ulong) - 1)) == 0); + wdp = (ulong *)bp->rp; + rxdp = ®s->rxdata; + wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong) + 1; + while (--wds > 0) + *wdp++ = *rxdp; + bp->wp = bp->rp + len; + + /* and push the Block upstream */ + if (ctlr->inited) + etheriq(edev, bp, 1); + else + freeb(bp); + + regs->intsts = Rxintrs; /* dismiss intr */ + coherence(); + regs->inten |= Rxintrs; + } + coherence(); + } + regs->inten |= Rxintrs; + coherence(); +} + +/* + * disable the stsclr bits in inten and write them to intsts to ack and dismiss + * the interrupt source. + */ +void +ackintr(Regs *regs, ulong stsclr) +{ + if (stsclr == 0) + return; + + regs->inten &= ~stsclr; + coherence(); + +// regs->intsts = stsclr; /* acknowledge & clear intr(s) */ +// coherence(); +} + +static void +smcinterrupt(Ureg*, void* arg) +{ + int junk; + unsigned intsts, intr; + Ctlr *ctlr; + Ether *edev; + Regs *regs; + + edev = arg; + ctlr = edev->ctlr; + ilock(&ctlr->imlock); + regs = ctlr->regs; + + gpioirqclr(); + + coherence(); /* back-to-back read/read delay per §6.2.2 */ + intsts = regs->intsts; + coherence(); + + intsts &= ~MASK(3); /* ignore gpio bits */ + if (0 && intsts == 0) { + coherence(); + iprint("smc: interrupt without a cause; insts %#ux (vs inten %#lux)\n", + intsts, regs->inten); + } + + intr = intsts & Rxintrs; + if(intr) { + /* disable interrupt sources; kproc/smcreceive will reenable */ + ackintr(regs, intr); + + ctlr->rintr++; + ctlr->gotinput = 1; +#ifdef USE_KPROCS + wakeup(&ctlr->rrendez); +#else + smcreceive(edev); +#endif + } + + while(((regs->txfifoinf & Txstsusedmask) >> Txstsusedshift) != 0) { + /* probably indicates tx completion, just toss it */ + junk = regs->txsts; /* pop tx sts */ + USED(junk); + coherence(); + } + + intr = intsts & Txintrs; + if (ctlr->gotoutput || intr) { + /* disable interrupt sources; kproc/smctransmit will reenable */ + ackintr(regs, intr); + + ctlr->tintr++; + ctlr->gotoutput = 1; +#ifdef USE_KPROCS + wakeup(&ctlr->trendez); +#else + smctransmit(edev); +#endif + } + + iunlock(&ctlr->imlock); +} + +static void +etherclock(void) +{ + smcinterrupt(nil, thisether); +} + +static int +smcmii(Ctlr *) +{ + return 0; +} + +static int +smcdetach(Ctlr* ctlr) +{ + Regs *regs; + + if (ctlr == nil || ctlr->regs == nil) + return -1; + regs = ctlr->regs; + /* verify that it's real by reading a few registers */ + switch (regs->id) { + case Vid9221: + break; + default: + print("smc: unknown chip id %#ux\n", regs->id); + return -1; + } + regs->inten = 0; /* no interrupts */ + regs->intsts = ~0; /* clear any pending */ + regs->gptcfg = 0; + coherence(); + regs->rxcfg = Rxdump; + regs->txcfg = Txsdump | Txddump; + regs->irqcfg &= ~Irqen; + coherence(); + return 0; +} + +static void +smcshutdown(Ether* ether) +{ + smcdetach(ether->ctlr); +} + +static void +powerwait(Regs *regs) +{ + long bound; + + regs->bytetest = 0; /* bring power on */ + for (bound = 400*Mhz; !(regs->pmtctl & Dready) && bound > 0; bound--) + ; + if (bound <= 0) + iprint("smc: pmtctl didn't come ready\n"); +} + +static int +smcreset(Ctlr* ctlr) +{ + int r; + Regs *regs; + static char zea[Eaddrlen]; + + regs = ctlr->regs; + powerwait(regs); + + if(smcdetach(ctlr)) + return -1; + + /* verify that it's real by reading a few registers */ + switch (regs->id) { + case Vid9221: + break; + default: + print("smc: unknown chip id %#ux\n", regs->id); + return -1; + } + if (regs->bytetest != 0x87654321) { + print("smc: bytetest reg %#p (%#lux) != 0x87654321\n", + ®s->bytetest, regs->bytetest); + return -1; + } + +#ifdef TODO /* read MAC from EEPROM */ +// int ctrl, i, pause, swdpio, txcw; + /* + * Snarf and set up the receive addresses. + * There are 16 addresses. The first should be the MAC address. + * The others are cleared and not marked valid (MS bit of Rah). + */ + for(i = Ea; i < Eaddrlen/2; i++){ + ctlr->ra[2*i] = ctlr->eeprom[i]; + ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8; + } + + /* + * Clear the Multicast Table Array. + * It's a 4096 bit vector accessed as 128 32-bit registers. + */ + memset(ctlr->mta, 0, sizeof(ctlr->mta)); + for(i = 0; i < 128; i++) + csr32w(ctlr, Mta+i*4, 0); +#endif + regs->hwcfg |= Mbo; + + /* don't overwrite existing ea */ +// if (memcmp(edev->ea, zea, Eaddrlen) == 0) +// memmove(edev->ea, ctlr->ra, Eaddrlen); + + r = ctlr->ra[3]<<24 | ctlr->ra[2]<<16 | ctlr->ra[1]<<8 | ctlr->ra[0]; + macwr(regs, Macaddrl, r); + macwr(regs, Macaddrh, ctlr->ra[5]<<8 | ctlr->ra[4]); + + /* turn on the controller */ + macwr(regs, Maccoe, 0); + regs->inten = 0; /* no interrupts yet */ + regs->intsts = ~0; /* clear any pending */ + regs->gptcfg = 0; + coherence(); + regs->rxcfg = Rxdump; + regs->txcfg = Txsdump | Txddump | Txon; + regs->fifoint = 72<<24; /* default values */ + macwr(regs, Maccr, Rxall | Rcvown | Fdpx | Mcpas | Txen | Rxen); + coherence(); /* back-to-back write/read delay per §6.2.1 */ + regs->irqcfg = 1<<24 | Irqen | Irqpushpull; /* deas for 10µs (linux) */ + coherence(); /* back-to-back write/read delay per §6.2.1 */ + regs->inten = Rxintrs | Txintrs; + coherence(); + + if(smcmii(ctlr) < 0) + return -1; + return 0; +} + +static void +smcpci(void) +{ + Ctlr *ctlr; + static int beenhere; + + if (beenhere) + return; + beenhere = 1; + + if (probeaddr(PHYSETHER) < 0) + return; + ctlr = malloc(sizeof(Ctlr)); + ctlr->id = Vid9221<<16 | 0x0424; /* smsc 9221 */ + ctlr->port = PHYSETHER; + ctlr->nic = (int *)PHYSETHER; + ctlr->regs = (Regs *)PHYSETHER; + + if(smcreset(ctlr)){ + free(ctlr); + return; + } + if(smcctlrhead != nil) + smcctlrtail->next = ctlr; + else + smcctlrhead = ctlr; + smcctlrtail = ctlr; +} + +static int +smcpnp(Ether* edev) +{ + Ctlr *ctlr; + static char zea[Eaddrlen]; + + if(smcctlrhead == nil) + smcpci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = smcctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + ctlr->edev = edev; /* point back to Ether* */ + edev->port = ctlr->port; + edev->irq = 34; +// TODO: verify speed (100Mb/s) and duplicity (full-duplex) + edev->mbps = 100; + + /* don't overwrite existing ea */ + if (memcmp(edev->ea, zea, Eaddrlen) == 0) + memmove(edev->ea, ctlr->ra, Eaddrlen); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = smcattach; + edev->transmit = smctransmitcall; + edev->interrupt = smcinterrupt; + edev->ifstat = smcifstat; +/* edev->ctl = smcctl; /* no ctl msgs supported */ + + edev->arg = edev; + edev->promiscuous = smcpromiscuous; + edev->multicast = smcmulticast; + edev->shutdown = smcshutdown; + return 0; +} + +void +ether9221link(void) +{ + addethercard("9221", smcpnp); +} diff --git a/sys/src/9/omap/etherif.h b/sys/src/9/omap/etherif.h new file mode 100755 index 000000000..dbc1721bf --- /dev/null +++ b/sys/src/9/omap/etherif.h @@ -0,0 +1,41 @@ +enum +{ + MaxEther = 4, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { + RWlock; + ISAConf; /* hardware info */ + + int ctlrno; + int minmtu; + int maxmtu; + + Netif; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + + void* ctlr; + uchar ea[Eaddrlen]; + void* address; + int irq; + + Queue* oq; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern ulong ethercrc(uchar*, int); +extern int parseether(uchar*, char*); + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) diff --git a/sys/src/9/omap/fns.h b/sys/src/9/omap/fns.h new file mode 100755 index 000000000..6fe173108 --- /dev/null +++ b/sys/src/9/omap/fns.h @@ -0,0 +1,178 @@ +#define checkmmu(a, b) +#define countpagerefs(a, b) + +#include "../port/portfns.h" + +extern int led(int, int); +extern void ledexit(int); +extern void delay(int); +extern void _uartputs(char*, int); +extern int _uartprint(char*, ...); + +#pragma varargck argpos _uartprint 1 + +extern void archreboot(void); +extern void archreset(void); +extern void cachedinv(void); +extern void cachedinvse(void*, int); +extern void cachedwb(void); +extern void cachedwbinv(void); +extern void cachedwbinvse(void*, int); +extern void cachedwbse(void*, int); +extern void cacheiinv(void); +extern void cacheinfo(int level, Memcache *cp); +extern void cacheuwbinv(void); +extern uintptr cankaddr(uintptr pa); +extern void chkmissing(void); +extern void clockshutdown(void); +extern int clz(ulong); +extern int cmpswap(long*, long, long); +extern void coherence(void); +extern void configscreengpio(void); +extern u32int controlget(void); +extern u32int cpctget(void); +extern u32int cpidget(void); +extern ulong cprd(int cp, int op1, int crn, int crm, int op2); +extern ulong cprdsc(int op1, int crn, int crm, int op2); +extern void cpuidprint(void); +extern void cpwr(int cp, int op1, int crn, int crm, int op2, ulong val); +extern void cpwrsc(int op1, int crn, int crm, int op2, ulong val); +#define cycles(ip) *(ip) = lcycles() +extern u32int dacget(void); +extern void dacput(u32int); +extern void dmainit(void); +extern int dmastart(void *, int, void *, int, uint, Rendez *, int *); +extern void dmatest(void); +extern u32int farget(void); +extern ulong fprd(int fpreg); +extern void fpwr(int fpreg, ulong val); +extern u32int fsrget(void); +extern u32int getscr(void); +extern u32int getpsr(void); +extern ulong getwayssets(void); +extern void intrsoff(void); +extern int isaconfig(char*, int, ISAConf*); +extern int isdmadone(int); +extern int ispow2(uvlong); +extern void kbdenable(void); +extern void l2cacheuinv(void); +extern void l2cacheuwb(void); +extern void l2cacheuwbinv(void); +extern void lastresortprint(char *buf, long bp); +extern int log2(ulong); +extern void machinit(void); +extern void mmuidmap(uintptr phys, int mbs); +extern void mmuinvalidate(void); /* 'mmu' or 'tlb'? */ +extern void mmuinvalidateaddr(u32int); /* 'mmu' or 'tlb'? */ +extern void mousectl(Cmdbuf *cb); +extern u32int pidget(void); +extern void pidput(u32int); +extern vlong probeaddr(uintptr); +extern void procrestore(Proc *); +extern void procsave(Proc*); +extern void procsetup(Proc*); +extern void _reset(void); +extern void screenclockson(void); +extern void screeninit(void); +extern void serialputs(char* s, int n); +extern void setcachelvl(int); +extern void setr13(int, u32int*); +extern int tas(void *); +extern u32int ttbget(void); +extern void ttbput(u32int); +extern void watchdoginit(void); + +extern int irqenable(int, void (*)(Ureg*, void*), void*, char*); +extern int irqdisable(int, void (*)(Ureg*, void*), void*, char*); +#define intrenable(i, f, a, b, n) irqenable((i), (f), (a), (n)) +#define intrdisable(i, f, a, b, n) irqdisable((i), (f), (a), (n)) +extern void vectors(void); +extern void vtable(void); + +/* dregs, going away */ +extern int inb(int); +extern void outb(int, int); + +/* + * Things called in main. + */ +extern void archconfinit(void); +extern void clockinit(void); +extern int i8250console(void); +extern void links(void); +extern void mmuinit(void); +extern void touser(uintptr); +extern void trapinit(void); + + +extern int fpiarm(Ureg*); +extern int fpudevprocio(Proc*, void*, long, uintptr, int); +extern void fpuinit(void); +extern void fpunoted(void); +extern void fpunotify(Ureg*); +extern void fpuprocrestore(Proc*); +extern void fpuprocsave(Proc*); +extern void fpusysprocsetup(Proc*); +extern void fpusysrfork(Ureg*); +extern void fpusysrforkchild(Proc*, Ureg*, Proc*); +extern int fpuemu(Ureg*); + +/* + * Miscellaneous machine dependent stuff. + */ +extern char* getenv(char*, char*, int); +char* getconf(char*); +uintptr mmukmap(uintptr, uintptr, usize); +uintptr mmukunmap(uintptr, uintptr, usize); +extern void* mmuuncache(void*, usize); +extern void* ucalloc(usize); +extern Block* ucallocb(int); +extern void* ucallocalign(usize size, int align, int span); +extern void ucfree(void*); +extern void ucfreeb(Block*); + +/* + * Things called from port. + */ +extern void delay(int); /* only scheddump() */ +extern int islo(void); +extern void microdelay(int); /* only edf.c */ +extern void evenaddr(uintptr); +extern void idlehands(void); +extern void setkernur(Ureg*, Proc*); /* only devproc.c */ +extern void* sysexecregs(uintptr, ulong, int); +extern void sysprocsetup(Proc*); + +/* + * PCI stuff. + */ + +int cas32(void*, u32int, u32int); +int tas32(void*); + +#define CASU(p, e, n) cas32((p), (u32int)(e), (u32int)(n)) +#define CASV(p, e, n) cas32((p), (u32int)(e), (u32int)(n)) +#define CASW(addr, exp, new) cas32((addr), (exp), (new)) +#define TAS(addr) tas32(addr) + +extern void forkret(void); +extern int userureg(Ureg*); +void* vmap(uintptr, usize); +void vunmap(void*, usize); + +extern void kexit(Ureg*); + +#define getpgcolor(a) 0 +#define kmapinval() + +#define PTR2UINT(p) ((uintptr)(p)) +#define UINT2PTR(i) ((void*)(i)) + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) + +#define KADDR(pa) UINT2PTR(KZERO | ((uintptr)(pa) & ~KSEGM)) +#define PADDR(va) PTR2UINT(PHYSDRAM | ((uintptr)(va) & ~KSEGM)) + +#define wave(c) *(ulong *)PHYSCONS = (c) + +#define MASK(v) ((1UL << (v)) - 1) /* mask `v' bits wide */ diff --git a/sys/src/9/omap/fpi.c b/sys/src/9/omap/fpi.c new file mode 100755 index 000000000..f341f2e4a --- /dev/null +++ b/sys/src/9/omap/fpi.c @@ -0,0 +1,300 @@ +/* + * Floating Point Interpreter. + * shamelessly stolen from an original by ark. + */ +#include "fpi.h" + +void +fpiround(Internal *i) +{ + unsigned long guard; + + guard = i->l & GuardMask; + i->l &= ~GuardMask; + if(guard > (LsBit>>1) || (guard == (LsBit>>1) && (i->l & LsBit))){ + i->l += LsBit; + if(i->l & CarryBit){ + i->l &= ~CarryBit; + i->h++; + if(i->h & CarryBit){ + if (i->h & 0x01) + i->l |= CarryBit; + i->l >>= 1; + i->h >>= 1; + i->e++; + } + } + } +} + +static void +matchexponents(Internal *x, Internal *y) +{ + int count; + + count = y->e - x->e; + x->e = y->e; + if(count >= 2*FractBits){ + x->l = x->l || x->h; + x->h = 0; + return; + } + if(count >= FractBits){ + count -= FractBits; + x->l = x->h|(x->l != 0); + x->h = 0; + } + while(count > 0){ + count--; + if(x->h & 0x01) + x->l |= CarryBit; + if(x->l & 0x01) + x->l |= 2; + x->l >>= 1; + x->h >>= 1; + } +} + +static void +shift(Internal *i) +{ + i->e--; + i->h <<= 1; + i->l <<= 1; + if(i->l & CarryBit){ + i->l &= ~CarryBit; + i->h |= 0x01; + } +} + +static void +normalise(Internal *i) +{ + while((i->h & HiddenBit) == 0) + shift(i); +} + +static void +renormalise(Internal *i) +{ + if(i->e < -2 * FractBits) + i->e = -2 * FractBits; + while(i->e < 1){ + i->e++; + if(i->h & 0x01) + i->l |= CarryBit; + i->h >>= 1; + i->l = (i->l>>1)|(i->l & 0x01); + } + if(i->e >= ExpInfinity) + SetInfinity(i); +} + +void +fpinormalise(Internal *x) +{ + if(!IsWeird(x) && !IsZero(x)) + normalise(x); +} + +void +fpiadd(Internal *x, Internal *y, Internal *i) +{ + Internal *t; + + i->s = x->s; + if(IsWeird(x) || IsWeird(y)){ + if(IsNaN(x) || IsNaN(y)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + if(x->e > y->e){ + t = x; + x = y; + y = t; + } + matchexponents(x, y); + i->e = x->e; + i->h = x->h + y->h; + i->l = x->l + y->l; + if(i->l & CarryBit){ + i->h++; + i->l &= ~CarryBit; + } + if(i->h & (HiddenBit<<1)){ + if(i->h & 0x01) + i->l |= CarryBit; + i->l = (i->l>>1)|(i->l & 0x01); + i->h >>= 1; + i->e++; + } + if(IsWeird(i)) + SetInfinity(i); +} + +void +fpisub(Internal *x, Internal *y, Internal *i) +{ + Internal *t; + + if(y->e < x->e + || (y->e == x->e && (y->h < x->h || (y->h == x->h && y->l < x->l)))){ + t = x; + x = y; + y = t; + } + i->s = y->s; + if(IsNaN(y)){ + SetQNaN(i); + return; + } + if(IsInfinity(y)){ + if(IsInfinity(x)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + matchexponents(x, y); + i->e = y->e; + i->h = y->h - x->h; + i->l = y->l - x->l; + if(i->l < 0){ + i->l += CarryBit; + i->h--; + } + if(i->h == 0 && i->l == 0) + SetZero(i); + else while(i->e > 1 && (i->h & HiddenBit) == 0) + shift(i); +} + +#define CHUNK (FractBits/2) +#define CMASK ((1<>CHUNK) & CMASK) +#define LO(x) ((short)(x) & CMASK) +#define SPILL(x) ((x)>>CHUNK) +#define M(x, y) ((long)a[x]*(long)b[y]) +#define C(h, l) (((long)((h) & CMASK)<s = x->s^y->s; + if(IsWeird(x) || IsWeird(y)){ + if(IsNaN(x) || IsNaN(y) || IsZero(x) || IsZero(y)) + SetQNaN(i); + else + SetInfinity(i); + return; + } + else if(IsZero(x) || IsZero(y)){ + SetZero(i); + return; + } + normalise(x); + normalise(y); + i->e = x->e + y->e - (ExpBias - 1); + + a[0] = HI(x->h); b[0] = HI(y->h); + a[1] = LO(x->h); b[1] = LO(y->h); + a[2] = HI(x->l); b[2] = HI(y->l); + a[3] = LO(x->l); b[3] = LO(y->l); + + c[6] = M(3, 3); + c[5] = M(2, 3) + M(3, 2) + SPILL(c[6]); + c[4] = M(1, 3) + M(2, 2) + M(3, 1) + SPILL(c[5]); + c[3] = M(0, 3) + M(1, 2) + M(2, 1) + M(3, 0) + SPILL(c[4]); + c[2] = M(0, 2) + M(1, 1) + M(2, 0) + SPILL(c[3]); + c[1] = M(0, 1) + M(1, 0) + SPILL(c[2]); + c[0] = M(0, 0) + SPILL(c[1]); + + f[0] = c[0]; + f[1] = C(c[1], c[2]); + f[2] = C(c[3], c[4]); + f[3] = C(c[5], c[6]); + + if((f[0] & HiddenBit) == 0){ + f[0] <<= 1; + f[1] <<= 1; + f[2] <<= 1; + f[3] <<= 1; + if(f[1] & CarryBit){ + f[0] |= 1; + f[1] &= ~CarryBit; + } + if(f[2] & CarryBit){ + f[1] |= 1; + f[2] &= ~CarryBit; + } + if(f[3] & CarryBit){ + f[2] |= 1; + f[3] &= ~CarryBit; + } + i->e--; + } + i->h = f[0]; + i->l = f[1]; + if(f[2] || f[3]) + i->l |= 1; + renormalise(i); +} + +void +fpidiv(Internal *x, Internal *y, Internal *i) +{ + i->s = x->s^y->s; + if(IsNaN(x) || IsNaN(y) + || (IsInfinity(x) && IsInfinity(y)) || (IsZero(x) && IsZero(y))){ + SetQNaN(i); + return; + } + else if(IsZero(x) || IsInfinity(y)){ + SetInfinity(i); + return; + } + else if(IsInfinity(x) || IsZero(y)){ + SetZero(i); + return; + } + normalise(x); + normalise(y); + i->h = 0; + i->l = 0; + i->e = y->e - x->e + (ExpBias + 2*FractBits - 1); + do{ + if(y->h > x->h || (y->h == x->h && y->l >= x->l)){ + i->l |= 0x01; + y->h -= x->h; + y->l -= x->l; + if(y->l < 0){ + y->l += CarryBit; + y->h--; + } + } + shift(y); + shift(i); + }while ((i->h & HiddenBit) == 0); + if(y->h || y->l) + i->l |= 0x01; + renormalise(i); +} + +int +fpicmp(Internal *x, Internal *y) +{ + if(IsNaN(x) && IsNaN(y)) + return 0; + if(IsInfinity(x) && IsInfinity(y)) + return y->s - x->s; + if(x->e == y->e && x->h == y->h && x->l == y->l) + return y->s - x->s; + if(x->e < y->e + || (x->e == y->e && (x->h < y->h || (x->h == y->h && x->l < y->l)))) + return y->s ? 1: -1; + return x->s ? -1: 1; +} diff --git a/sys/src/9/omap/fpi.h b/sys/src/9/omap/fpi.h new file mode 100755 index 000000000..abaa7c120 --- /dev/null +++ b/sys/src/9/omap/fpi.h @@ -0,0 +1,61 @@ +typedef long Word; +typedef unsigned long Single; +typedef struct { + unsigned long l; + unsigned long h; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff --git a/sys/src/9/omap/fpiarm.c b/sys/src/9/omap/fpiarm.c new file mode 100755 index 000000000..063d10c52 --- /dev/null +++ b/sys/src/9/omap/fpiarm.c @@ -0,0 +1,576 @@ +/* + * this doesn't attempt to implement ARM floating-point properties + * that aren't visible in the Inferno environment. + * all arithmetic is done in double precision. + * the FP trap status isn't updated. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include "ureg.h" + +#include "arm.h" +#include "fpi.h" + +/* undef this if correct kernel r13 isn't in Ureg; + * check calculation in fpiarm below + */ + + +#define REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)])) +#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7]) + +typedef struct FP2 FP2; +typedef struct FP1 FP1; + +struct FP2 { + char* name; + void (*f)(Internal, Internal, Internal*); +}; + +struct FP1 { + char* name; + void (*f)(Internal*, Internal*); +}; + +enum { + N = 1<<31, + Z = 1<<30, + C = 1<<29, + V = 1<<28, + REGPC = 15, +}; + +enum { + fpemudebug = 0, +}; + +#undef OFR +#define OFR(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + OFR(r0), OFR(r1), OFR(r2), OFR(r3), + OFR(r4), OFR(r5), OFR(r6), OFR(r7), + OFR(r8), OFR(r9), OFR(r10), OFR(r11), + OFR(r12), OFR(r13), OFR(r14), OFR(pc), +}; + +static Internal fpconst[8] = { /* indexed by op&7 */ + /* s, e, l, h */ + {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */ + {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */ + {0, 0x400, 0x00000000, 0x0C000000}, /* 3.0 */ + {0, 0x401, 0x00000000, 0x08000000}, /* 4.0 */ + {0, 0x401, 0x00000000, 0x0A000000}, /* 5.0 */ + {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */ + {0, 0x402, 0x00000000, 0x0A000000}, /* 10.0 */ +}; + +/* + * arm binary operations + */ + +static void +fadd(Internal m, Internal n, Internal *d) +{ + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsub(Internal m, Internal n, Internal *d) +{ + m.s ^= 1; + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsubr(Internal m, Internal n, Internal *d) +{ + n.s ^= 1; + (n.s == m.s? fpiadd: fpisub)(&n, &m, d); +} + +static void +fmul(Internal m, Internal n, Internal *d) +{ + fpimul(&m, &n, d); +} + +static void +fdiv(Internal m, Internal n, Internal *d) +{ + fpidiv(&m, &n, d); +} + +static void +fdivr(Internal m, Internal n, Internal *d) +{ + fpidiv(&n, &m, d); +} + +/* + * arm unary operations + */ + +static void +fmov(Internal *m, Internal *d) +{ + *d = *m; +} + +static void +fmovn(Internal *m, Internal *d) +{ + *d = *m; + d->s ^= 1; +} + +static void +fabsf(Internal *m, Internal *d) +{ + *d = *m; + d->s = 0; +} + +static void +frnd(Internal *m, Internal *d) +{ + short e; + + (m->s? fsub: fadd)(fpconst[6], *m, d); + if(IsWeird(d)) + return; + fpiround(d); + e = (d->e - ExpBias) + 1; + if(e <= 0) + SetZero(d); + else if(e > FractBits){ + if(e < 2*FractBits) + d->l &= ~((1<<(2*FractBits - e))-1); + }else{ + d->l = 0; + if(e < FractBits) + d->h &= ~((1<<(FractBits-e))-1); + } +} + +static FP1 optab1[16] = { /* Fd := OP Fm */ +[0] {"MOVF", fmov}, +[1] {"NEGF", fmovn}, +[2] {"ABSF", fabsf}, +[3] {"RNDF", frnd}, +[4] {"SQTF", /*fsqt*/0}, +/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */ +/* URD and NRM aren't implemented */ +}; + +static FP2 optab2[16] = { /* Fd := Fn OP Fm */ +[0] {"ADDF", fadd}, +[1] {"MULF", fmul}, +[2] {"SUBF", fsub}, +[3] {"RSUBF", fsubr}, +[4] {"DIVF", fdiv}, +[5] {"RDIVF", fdivr}, +/* POW, RPW deprecated */ +[8] {"REMF", /*frem*/0}, +[9] {"FMF", fmul}, /* fast multiply */ +[10] {"FDV", fdiv}, /* fast divide */ +[11] {"FRD", fdivr}, /* fast reverse divide */ +/* POL deprecated */ +}; + +static ulong +fcmp(Internal *n, Internal *m) +{ + int i; + Internal rm, rn; + + if(IsWeird(m) || IsWeird(n)){ + /* BUG: should trap if not masked */ + return V|C; + } + rn = *n; + rm = *m; + fpiround(&rn); + fpiround(&rm); + i = fpicmp(&rn, &rm); + if(i > 0) + return C; + else if(i == 0) + return C|Z; + else + return N; +} + +static void +fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp) +{ + void *mem; + + mem = (void*)ea; + (*f)(&FR(ufp, d), mem); + if(fpemudebug) + print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d); +} + +static void +fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp) +{ + Internal tmp; + void *mem; + + mem = (void*)ea; + tmp = FR(ufp, s); + if(fpemudebug) + print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea); + (*f)(mem, &tmp); +} + +static int +condok(int cc, int c) +{ + switch(c){ + case 0: /* Z set */ + return cc&Z; + case 1: /* Z clear */ + return (cc&Z) == 0; + case 2: /* C set */ + return cc&C; + case 3: /* C clear */ + return (cc&C) == 0; + case 4: /* N set */ + return cc&N; + case 5: /* N clear */ + return (cc&N) == 0; + case 6: /* V set */ + return cc&V; + case 7: /* V clear */ + return (cc&V) == 0; + case 8: /* C set and Z clear */ + return cc&C && (cc&Z) == 0; + case 9: /* C clear or Z set */ + return (cc&C) == 0 || cc&Z; + case 10: /* N set and V set, or N clear and V clear */ + return (~cc&(N|V))==0 || (cc&(N|V)) == 0; + case 11: /* N set and V clear, or N clear and V set */ + return (cc&(N|V))==N || (cc&(N|V))==V; + case 12: /* Z clear, and either N set and V set or N clear and V clear */ + return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0); + case 13: /* Z set, or N set and V clear or N clear and V set */ + return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V; + case 14: /* always */ + return 1; + case 15: /* never (reserved) */ + return 0; + } + return 0; /* not reached */ +} + +static void +unimp(ulong pc, ulong op) +{ + char buf[60]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op); + if(fpemudebug) + print("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +static void +fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) +{ + int rn, rd, tag, o; + long off; + ulong ea; + Internal tmp, *fm, *fn; + + /* note: would update fault status here if we noted numeric exceptions */ + + /* + * LDF, STF; 10.1.1 + */ + if(((op>>25)&7) == 6){ + if(op & (1<<22)) + unimp(pc, op); /* packed or extended */ + rn = (op>>16)&0xF; + off = (op&0xFF)<<2; + if((op & (1<<23)) == 0) + off = -off; + ea = REG(ur, rn); + if(rn == REGPC) + ea += 8; + if(op & (1<<24)) + ea += off; + rd = (op>>12)&7; + if(op & (1<<20)){ + if(op & (1<<15)) + fld(fpid2i, rd, ea, 8, ufp); + else + fld(fpis2i, rd, ea, 4, ufp); + }else{ + if(op & (1<<15)) + fst(fpii2d, ea, rd, 8, ufp); + else + fst(fpii2s, ea, rd, 4, ufp); + } + if((op & (1<<24)) == 0) + ea += off; + if(op & (1<<21)) + REG(ur, rn) = ea; + return; + } + + /* + * CPRT/transfer, 10.3 + */ + if(op & (1<<4)){ + rd = (op>>12) & 0xF; + + /* + * compare, 10.3.1 + */ + if(rd == 15 && op & (1<<20)){ + rn = (op>>16)&7; + fn = &FR(ufp, rn); + if(op & (1<<3)){ + fm = &fpconst[op&7]; + if(fpemudebug) + tag = 'C'; + }else{ + fm = &FR(ufp, op&7); + if(fpemudebug) + tag = 'F'; + } + switch((op>>21)&7){ + default: + unimp(pc, op); + case 4: /* CMF: Fn :: Fm */ + case 6: /* CMFE: Fn :: Fm (with exception) */ + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, fm); + break; + case 5: /* CNF: Fn :: -Fm */ + case 7: /* CNFE: Fn :: -Fm (with exception) */ + tmp = *fm; + tmp.s ^= 1; + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, &tmp); + break; + } + if(fpemudebug) + print("CMPF %c%d,F%ld =%#lux\n", + tag, rn, op&7, ur->psr>>28); + return; + } + + /* + * other transfer, 10.3 + */ + switch((op>>20)&0xF){ + default: + unimp(pc, op); + case 0: /* FLT */ + rn = (op>>16) & 7; + fpiw2i(&FR(ufp, rn), ®(ur, rd)); + if(fpemudebug) + print("MOVW[FD] R%d, F%d\n", rd, rn); + break; + case 1: /* FIX */ + if(op & (1<<3)) + unimp(pc, op); + rn = op & 7; + tmp = FR(ufp, rn); + fpii2w(®(ur, rd), &tmp); + if(fpemudebug) + print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(ur, rd)); + break; + case 2: /* FPSR := Rd */ + ufp->status = REG(ur, rd); + if(fpemudebug) + print("MOVW R%d, FPSR\n", rd); + break; + case 3: /* Rd := FPSR */ + REG(ur, rd) = ufp->status; + if(fpemudebug) + print("MOVW FPSR, R%d\n", rd); + break; + case 4: /* FPCR := Rd */ + ufp->control = REG(ur, rd); + if(fpemudebug) + print("MOVW R%d, FPCR\n", rd); + break; + case 5: /* Rd := FPCR */ + REG(ur, rd) = ufp->control; + if(fpemudebug) + print("MOVW FPCR, R%d\n", rd); + break; + } + return; + } + + /* + * arithmetic + */ + + if(op & (1<<3)){ /* constant */ + fm = &fpconst[op&7]; + if(fpemudebug) + tag = 'C'; + }else{ + fm = &FR(ufp, op&7); + if(fpemudebug) + tag = 'F'; + } + rd = (op>>12)&7; + o = (op>>20)&0xF; + if(op & (1<<15)){ /* monadic */ + FP1 *fp; + fp = &optab1[o]; + if(fp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd); + (*fp->f)(fm, &FR(ufp, rd)); + } else { + FP2 *fp; + fp = &optab2[o]; + if(fp->f == nil) + unimp(pc, op); + rn = (op>>16)&7; + if(fpemudebug) + print("%s %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd); + (*fp->f)(*fm, FR(ufp, rn), &FR(ufp, rd)); + } +} + +void +casemu(ulong pc, ulong op, Ureg *ur) +{ + ulong *rp, ro, rn, *rd; + + USED(pc); + + rp = (ulong*)ur; + ro = rp[op>>16 & 0x7]; + rn = rp[op>>0 & 0x7]; + rd = rp + (op>>12 & 0x7); + rp = (ulong*)*rd; + validaddr((ulong)rp, 4, 1); + splhi(); + if(*rd = (*rp == ro)) + *rp = rn; + spllo(); +} + +int ldrexvalid; + +void +ldrex(ulong pc, ulong op, Ureg *ur) +{ + ulong *rp, *rd, *addr; + + USED(pc); + + rp = (ulong*)ur; + rd = rp + (op>>16 & 0x7); + addr = (ulong*)*rd; + validaddr((ulong)addr, 4, 0); + ldrexvalid = 1; + rp[op>>12 & 0x7] = *addr; + if(fpemudebug) + print("ldrex, r%ld = [r%ld]@0x%8.8p = 0x%8.8lux", + op>>12 & 0x7, op>>16 & 0x7, addr, rp[op>>12 & 0x7]); +} + +void +strex(ulong pc, ulong op, Ureg *ur) +{ + ulong *rp, rn, *rd, *addr; + + USED(pc); + + rp = (ulong*)ur; + rd = rp + (op>>16 & 0x7); + rn = rp[op>>0 & 0x7]; + addr = (ulong*)*rd; + validaddr((ulong)addr, 4, 1); + splhi(); + if(ldrexvalid){ + if(fpemudebug) + print("strex valid, [r%ld]@0x%8.8p = r%ld = 0x%8.8lux", + op>>16 & 0x7, addr, op>>0 & 0x7, rn); + *addr = rn; + ldrexvalid = 0; + rp[op>>12 & 0x7] = 0; + }else{ + if(fpemudebug) + print("strex invalid, r%ld = 1", op>>16 & 0x7); + rp[op>>12 & 0x7] = 1; + } + spllo(); +} + +struct { + ulong opc; + ulong mask; + void (*f)(ulong, ulong, Ureg*); +} specialopc[] = { + { 0x01900f9f, 0x0ff00fff, ldrex }, + { 0x01800f90, 0x0ff00ff0, strex }, + { 0x0ed00100, 0x0ef08100, casemu }, + { 0x00000000, 0x00000000, nil } +}; + +/* + * returns the number of FP instructions emulated + */ +int +fpiarm(Ureg *ur) +{ + ulong op, o; + FPsave *ufp; + int i, n; + + if(up == nil) + panic("fpiarm not in a process"); + ufp = &up->fpsave; + /* because all the state is in the proc structure, + * it need not be saved/restored + */ + if(up->fpstate != FPactive){ +// assert(sizeof(Internal) == sizeof(ufp->regs[0])); + up->fpstate = FPactive; + ufp->control = 0; + ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */ + for(n = 0; n < 8; n++) + FR(ufp, n) = fpconst[0]; + } + for(n=0; ;n++){ + validaddr(ur->pc, 4, 0); + op = *(ulong*)(ur->pc); + if(fpemudebug) + print("%#lux: %#8.8lux ", ur->pc, op); + o = (op>>24) & 0xF; + if(condok(ur->psr, op>>28)){ + for(i = 0; specialopc[i].f; i++) + if((op & specialopc[i].mask) == specialopc[i].opc) + break; + if(specialopc[i].f) + specialopc[i].f(ur->pc, op, ur); + else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC) + break; + else + fpemu(ur->pc, op, ur, ufp); + }else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC) + break; + ur->pc += 4; + } + if(fpemudebug) print("\n"); + return n; +} diff --git a/sys/src/9/omap/fpimem.c b/sys/src/9/omap/fpimem.c new file mode 100755 index 000000000..627ab6355 --- /dev/null +++ b/sys/src/9/omap/fpimem.c @@ -0,0 +1,136 @@ +#include "fpi.h" + +/* + * the following routines depend on memory format, not the machine + */ + +void +fpis2i(Internal *i, void *v) +{ + Single *s = v; + + i->s = (*s & 0x80000000) ? 1: 0; + if((*s & ~0x80000000) == 0){ + SetZero(i); + return; + } + i->e = ((*s>>23) & 0x00FF) - SingleExpBias + ExpBias; + i->h = (*s & 0x007FFFFF)<<(1+NGuardBits); + i->l = 0; + if(i->e) + i->h |= HiddenBit; + else + i->e++; +} + +void +fpid2i(Internal *i, void *v) +{ + Double *d = v; + + i->s = (d->h & 0x80000000) ? 1: 0; + i->e = (d->h>>20) & 0x07FF; + i->h = ((d->h & 0x000FFFFF)<<(4+NGuardBits))|((d->l>>25) & 0x7F); + i->l = (d->l & 0x01FFFFFF)<e) + i->h |= HiddenBit; + else + i->e++; +} + +void +fpiw2i(Internal *i, void *v) +{ + Word w, word = *(Word*)v; + short e; + + if(word < 0){ + i->s = 1; + word = -word; + } + else + i->s = 0; + if(word == 0){ + SetZero(i); + return; + } + if(word > 0){ + for (e = 0, w = word; w; w >>= 1, e++) + ; + } else + e = 32; + if(e > FractBits){ + i->h = word>>(e - FractBits); + i->l = (word & ((1<<(e - FractBits)) - 1))<<(2*FractBits - e); + } + else { + i->h = word<<(FractBits - e); + i->l = 0; + } + i->e = (e - 1) + ExpBias; +} + +void +fpii2s(void *v, Internal *i) +{ + short e; + Single *s = (Single*)v; + + fpiround(i); + if(i->h & HiddenBit) + i->h &= ~HiddenBit; + else + i->e--; + *s = i->s ? 0x80000000: 0; + e = i->e; + if(e < ExpBias){ + if(e <= (ExpBias - SingleExpBias)) + return; + e = SingleExpBias - (ExpBias - e); + } + else if(e >= (ExpBias + (SingleExpMax-SingleExpBias))){ + *s |= SingleExpMax<<23; + return; + } + else + e = SingleExpBias + (e - ExpBias); + *s |= (e<<23)|(i->h>>(1+NGuardBits)); +} + +void +fpii2d(void *v, Internal *i) +{ + Double *d = (Double*)v; + + fpiround(i); + if(i->h & HiddenBit) + i->h &= ~HiddenBit; + else + i->e--; + i->l = ((i->h & GuardMask)<<25)|(i->l>>NGuardBits); + i->h >>= NGuardBits; + d->h = i->s ? 0x80000000: 0; + d->h |= (i->e<<20)|((i->h & 0x00FFFFFF)>>4); + d->l = (i->h<<28)|i->l; +} + +void +fpii2w(Word *word, Internal *i) +{ + Word w; + short e; + + fpiround(i); + e = (i->e - ExpBias) + 1; + if(e <= 0) + w = 0; + else if(e > 31) + w = 0x7FFFFFFF; + else if(e > FractBits) + w = (i->h<<(e - FractBits))|(i->l>>(2*FractBits - e)); + else + w = i->h>>(FractBits-e); + if(i->s) + w = -w; + *word = w; +} diff --git a/sys/src/9/omap/init9.s b/sys/src/9/omap/init9.s new file mode 100755 index 000000000..1d7f2bec3 --- /dev/null +++ b/sys/src/9/omap/init9.s @@ -0,0 +1,25 @@ +/* + * This is the same as the C programme: + * + * void + * main(char* argv0) + * { + * startboot(argv0, &argv0); + * } + * + * It is in assembler because SB needs to be + * set and doing this in C drags in too many + * other routines. + */ +TEXT main(SB), 1, $8 + MOVW $setR12(SB), R12 /* load the SB */ + MOVW $boot(SB), R0 + + ADD $12, R13, R1 /* pointer to 0(FP) */ + + MOVW R0, 4(R13) /* pass argc, argv */ + MOVW R1, 8(R13) + + BL startboot(SB) +_loop: + B _loop diff --git a/sys/src/9/omap/io.h b/sys/src/9/omap/io.h new file mode 100755 index 000000000..19afa7670 --- /dev/null +++ b/sys/src/9/omap/io.h @@ -0,0 +1,82 @@ +/* + * the ``general-purpose'' memory controller. + * only works with flash memory. + */ + +enum { + /* syscfg bits */ + Idlemask = MASK(2) << 3, + Noidle = 1 << 3, + + /* config bits */ + Postnandwrites = 1<<0, /* force nand reg. writes to be posted */ + + /* indices of cscfg[].cfg[] */ + Csctl = 1 - 1, /* chip-select signal ctl */ + Csmap = 7 - 1, /* chip-select addr map cfg */ + + /* Csctl bits */ + Muxadddata = 1 << 9, + Devtypemask = MASK(2) << 10, + Devtypenor = 0 << 10, + Devtypenand = 2 << 10, + Devsizemask = 1 << 12, + Devsize8 = 0 << 12, + Devsize16 = 1 << 12, + Writesync = 1 << 27, + Readsync = 1 << 29, + + /* Csmap bits */ + Csvalid = 1 << 6, + MB16 = 017 << 8, /* 16MB size */ + MB128 = 010 << 8, /* 128MB size */ +}; + +typedef struct Gpmc Gpmc; +typedef struct Gpmccs Gpmccs; + +/* + * configuration for non-dram (e.g., flash) memory + */ +struct Gpmc { /* hw registers */ + uchar _pad0[0x10]; + ulong syscfg; + ulong syssts; + ulong irqsts; + ulong irqenable; + uchar _pad1[0x40 - 0x20]; + ulong tmout_ctl; + ulong erraddr; + ulong errtype; + ulong _pad7; + ulong config; + ulong sts; + uchar _pad2[0x60 - 0x58]; + + /* chip-select config */ + struct Gpmccs { + ulong cfg[7]; + ulong nandcmd; + ulong nandaddr; + ulong nanddata; + ulong _pad6[2]; + } cscfg[8]; + + /* prefetch */ + ulong prefcfg[2]; + ulong _pad8; + ulong prefctl; + ulong prefsts; + + /* ecc */ + ulong ecccfg; + ulong eccctl; + ulong eccsize; + ulong eccres[9]; + uchar _pad3[0x240 - 0x224]; + + /* bch */ + ulong bchres[8][4]; + uchar _pad4[0x2d0 - 0x2c0]; + ulong bchswdata; +}; diff --git a/sys/src/9/omap/kbd.c b/sys/src/9/omap/kbd.c new file mode 100755 index 000000000..e81b352d2 --- /dev/null +++ b/sys/src/9/omap/kbd.c @@ -0,0 +1,410 @@ +/* + * simulated keyboard input for omap35 with no keyboard (except via uart or usb) + * + * gutted version of ps2 version from ../pc + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +enum { + Spec= 0xF800, /* Unicode private space */ + PF= Spec|0x20, /* num pad function key */ + View= Spec|0x00, /* view (shift window up) */ + KF= 0xF000, /* function key (begin Unicode private space) */ + Shift= Spec|0x60, + Break= Spec|0x61, + Ctrl= Spec|0x62, + Latin= Spec|0x63, + Caps= Spec|0x64, + Num= Spec|0x65, + Middle= Spec|0x66, + Altgr= Spec|0x67, + Kmouse= Spec|0x100, + No= 0x00, /* peter */ + + Home= KF|13, + Up= KF|14, + Pgup= KF|15, + Print= KF|16, + Left= KF|17, + Right= KF|18, + End= KF|24, + Down= View, + Pgdown= KF|19, + Ins= KF|20, + Del= 0x7F, + Scroll= KF|21, + + Nscan= 128, + + Int= 0, /* kbscans indices */ + Ext, + Nscans, +}; + +/* + * The codes at 0x79 and 0x7b are produced by the PFU Happy Hacking keyboard. + * A 'standard' keyboard doesn't produce anything above 0x58. + */ +Rune kbtab[Nscan] = +{ +[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6', +[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t', +[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', +[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's', +[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', +[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v', +[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, View, No, Up, No, No, No, No, +}; + +Rune kbtabshift[Nscan] = +{ +[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^', +[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t', +[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', +[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S', +[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', +[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V', +[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, Up, No, Up, No, No, No, No, +}; + +Rune kbtabesc1[Nscan] = +{ +[0x00] No, No, No, No, No, No, No, No, +[0x08] No, No, No, No, No, No, No, No, +[0x10] No, No, No, No, No, No, No, No, +[0x18] No, No, No, No, '\n', Ctrl, No, No, +[0x20] No, No, No, No, No, No, No, No, +[0x28] No, No, Shift, No, No, No, No, No, +[0x30] No, No, No, No, No, '/', No, Print, +[0x38] Altgr, No, No, No, No, No, No, No, +[0x40] No, No, No, No, No, No, Break, Home, +[0x48] Up, Pgup, No, Left, No, Right, No, End, +[0x50] Down, Pgdown, Ins, Del, No, No, No, No, +[0x58] No, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, Up, No, No, No, No, No, No, +}; + +Rune kbtabaltgr[Nscan] = +{ +[0x00] No, No, No, No, No, No, No, No, +[0x08] No, No, No, No, No, No, No, No, +[0x10] No, No, No, No, No, No, No, No, +[0x18] No, No, No, No, '\n', Ctrl, No, No, +[0x20] No, No, No, No, No, No, No, No, +[0x28] No, No, Shift, No, No, No, No, No, +[0x30] No, No, No, No, No, '/', No, Print, +[0x38] Altgr, No, No, No, No, No, No, No, +[0x40] No, No, No, No, No, No, Break, Home, +[0x48] Up, Pgup, No, Left, No, Right, No, End, +[0x50] Down, Pgdown, Ins, Del, No, No, No, No, +[0x58] No, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, Up, No, No, No, No, No, No, +}; + +Rune kbtabctrl[Nscan] = +{ +[0x00] No, '', '', '', '', '', '', '', +[0x08] '', '', '', '', ' ', '', '\b', '\t', +[0x10] '', '', '', '', '', '', '', '\t', +[0x18] '', '', '', '', '\n', Ctrl, '', '', +[0x20] '', '', '', '\b', '\n', ' ', ' ', '', +[0x28] '', No, Shift, '', '', '', '', '', +[0x30] '', '', ' ', ' ', '', '', Shift, '\n', +[0x38] Latin, No, Ctrl, '', '', '', '', '', +[0x40] '', '', ' ', ' ', '', '', '', '', +[0x48] '', '', ' ', '', '', '', ' ', '', +[0x50] '', '', '', '', No, No, No, '', +[0x58] ' ', No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, '', No, '\b', No, No, No, No, +}; + +int mouseshifted; +void (*kbdmouse)(int); + +static int kdebug; + +typedef struct Kbscan Kbscan; +struct Kbscan { + int esc1; + int esc2; + int alt; + int altgr; + int caps; + int ctl; + int num; + int shift; + int collecting; + int nk; + Rune kc[5]; + int buttons; +}; + +Kbscan kbscans[Nscans]; /* kernel and external scan code state */ + +/* + * Scan code processing + */ +void +kbdputsc(int c, int external) +{ + int i, keyup; + Kbscan *kbscan; + + if(external) + kbscan = &kbscans[Ext]; + else + kbscan = &kbscans[Int]; + + if(kdebug) + print("sc %x ms %d\n", c, mouseshifted); + /* + * e0's is the first of a 2 character sequence, e1 the first + * of a 3 character sequence (on the safari) + */ + if(c == 0xe0){ + kbscan->esc1 = 1; + return; + } else if(c == 0xe1){ + kbscan->esc2 = 2; + return; + } + + keyup = c & 0x80; + c &= 0x7f; + if(c > sizeof kbtab){ + c |= keyup; + if(c != 0xFF) /* these come fairly often: CAPSLOCK U Y */ + print("unknown key %ux\n", c); + return; + } + + if(kbscan->esc1){ + c = kbtabesc1[c]; + kbscan->esc1 = 0; + } else if(kbscan->esc2){ + kbscan->esc2--; + return; + } else if(kbscan->shift) + c = kbtabshift[c]; + else if(kbscan->altgr) + c = kbtabaltgr[c]; + else if(kbscan->ctl) + c = kbtabctrl[c]; + else + c = kbtab[c]; + + if(kbscan->caps && c<='z' && c>='a') + c += 'A' - 'a'; + + /* + * keyup only important for shifts + */ + if(keyup){ + switch(c){ + case Latin: + kbscan->alt = 0; + break; + case Shift: + kbscan->shift = 0; + mouseshifted = 0; + if(kdebug) + print("shiftclr\n"); + break; + case Ctrl: + kbscan->ctl = 0; + break; + case Altgr: + kbscan->altgr = 0; + break; + case Kmouse|1: + case Kmouse|2: + case Kmouse|3: + case Kmouse|4: + case Kmouse|5: + kbscan->buttons &= ~(1<<(c-Kmouse-1)); + if(kbdmouse) + kbdmouse(kbscan->buttons); + break; + } + return; + } + + /* + * normal character + */ + if(!(c & (Spec|KF))){ + if(kbscan->ctl) + if(kbscan->alt && c == Del) + exit(0); + if(!kbscan->collecting){ + kbdputc(kbdq, c); + return; + } + kbscan->kc[kbscan->nk++] = c; + c = latin1(kbscan->kc, kbscan->nk); + if(c < -1) /* need more keystrokes */ + return; + if(c != -1) /* valid sequence */ + kbdputc(kbdq, c); + else /* dump characters */ + for(i=0; ink; i++) + kbdputc(kbdq, kbscan->kc[i]); + kbscan->nk = 0; + kbscan->collecting = 0; + return; + } else { + switch(c){ + case Caps: + kbscan->caps ^= 1; + return; + case Num: + kbscan->num ^= 1; + return; + case Shift: + kbscan->shift = 1; + if(kdebug) + print("shift\n"); + mouseshifted = 1; + return; + case Latin: + kbscan->alt = 1; + /* + * VMware and Qemu use Ctl-Alt as the key combination + * to make the VM give up keyboard and mouse focus. + * This has the unfortunate side effect that when you + * come back into focus, Plan 9 thinks you want to type + * a compose sequence (you just typed alt). + * + * As a clumsy hack around this, we look for ctl-alt + * and don't treat it as the start of a compose sequence. + */ + if(!kbscan->ctl){ + kbscan->collecting = 1; + kbscan->nk = 0; + } + return; + case Ctrl: + kbscan->ctl = 1; + return; + case Altgr: + kbscan->altgr = 1; + return; + case Kmouse|1: + case Kmouse|2: + case Kmouse|3: + case Kmouse|4: + case Kmouse|5: + kbscan->buttons |= 1<<(c-Kmouse-1); + if(kbdmouse) + kbdmouse(kbscan->buttons); + return; + case KF|11: + print("kbd debug on, F12 turns it off\n"); + kdebug = 1; + break; + case KF|12: + kdebug = 0; + break; + } + } + kbdputc(kbdq, c); +} + +void +kbdenable(void) +{ +#ifdef notdef + kbdq = qopen(4*1024, 0, 0, 0); + if(kbdq == nil) + panic("kbdinit"); + qnoblock(kbdq, 1); +#endif + kbscans[Int].num = 0; +} + +void +kbdputmap(ushort m, ushort scanc, Rune r) +{ + if(scanc >= Nscan) + error(Ebadarg); + switch(m) { + default: + error(Ebadarg); + case 0: + kbtab[scanc] = r; + break; + case 1: + kbtabshift[scanc] = r; + break; + case 2: + kbtabesc1[scanc] = r; + break; + case 3: + kbtabaltgr[scanc] = r; + break; + case 4: + kbtabctrl[scanc] = r; + break; + } +} + +int +kbdgetmap(uint offset, int *t, int *sc, Rune *r) +{ + if ((int)offset < 0) + error(Ebadarg); + *t = offset/Nscan; + *sc = offset%Nscan; + switch(*t) { + default: + return 0; + case 0: + *r = kbtab[*sc]; + return 1; + case 1: + *r = kbtabshift[*sc]; + return 1; + case 2: + *r = kbtabesc1[*sc]; + return 1; + case 3: + *r = kbtabaltgr[*sc]; + return 1; + case 4: + *r = kbtabctrl[*sc]; + return 1; + } +} diff --git a/sys/src/9/omap/l.s b/sys/src/9/omap/l.s new file mode 100755 index 000000000..9950cd2ad --- /dev/null +++ b/sys/src/9/omap/l.s @@ -0,0 +1,569 @@ +/* + * ti omap3530 SoC machine assist + * arm cortex-a8 processor + * + * loader uses R11 as scratch. + * R9 and R10 are used for `extern register' variables. + * + * ARM v7 arch. ref. man. §B1.3.3 that we don't need barriers + * around moves to CPSR. + */ + +#include "arm.s" + +/* + * MCR and MRC are counter-intuitively named. + * MCR coproc, opcode1, Rd, CRn, CRm[, opcode2] # arm -> coproc + * MRC coproc, opcode1, Rd, CRn, CRm[, opcode2] # coproc -> arm + */ + +/* + * Entered here from Das U-Boot or another Plan 9 kernel with MMU disabled. + * Until the MMU is enabled it is OK to call functions provided + * they are within ±32MiB relative and do not require any + * local variables or more than one argument (i.e. there is + * no stack). + */ +TEXT _start(SB), 1, $-4 + MOVW $setR12(SB), R12 /* load the SB */ + SUB $KZERO, R12 + ADD $PHYSDRAM, R12 + + /* SVC mode, interrupts disabled */ + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 + MOVW R1, CPSR + BARRIERS + + DELAY(printloopret, 1) +WAVE('\r') + DELAY(printloopnl, 1) +WAVE('\n') + /* + * work around errata + */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + ORR $(CpACissue1|CpACldstissue1), R1 /* fight omap35x errata 3.1.1.9 */ + ORR $CpACibe, R1 /* enable cp15 invalidate */ + ORR $CpACl1pe, R1 /* enable l1 parity checking */ + ORR $CpCalign, R1 /* catch alignment errors */ + BIC $CpACasa, R1 /* no speculative accesses */ + /* go faster with fewer restrictions */ + BIC $(CpACcachenopipe|CpACcp15serial|CpACcp15waitidle|CpACcp15pipeflush), R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + ISB + + MRC CpSC, 1, R1, C(CpCLD), C(CpCLDl2), CpCLDl2aux + ORR $CpCl2nowralloc, R1 /* fight cortex errata 460075 */ + ORR $(CpCl2ecc|CpCl2eccparity), R1 +#ifdef TEDIUM + /* + * I don't know why this clobbers the system, but I'm tired + * of arguing with this fussy processor. To hell with it. + */ + MCR CpSC, 1, R1, C(CpCLD), C(CpCLDl2), CpCLDl2aux + ISB +#endif + DELAY(printloops, 1) +WAVE('P') + /* + * disable the MMU & caches + */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + BIC $(CpCdcache|CpCicache|CpCmmu), R1 + ORR $CpCsbo, R1 + BIC $CpCsbz, R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + ISB + + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + BIC $CpACl2en, R1 /* turn l2 cache off */ + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + ISB + +WAVE('l') + DELAY(printloop3, 1) + +WAVE('a') + /* clear Mach */ + MOVW $PADDR(MACHADDR), R4 /* address of Mach */ + MOVW $0, R0 +_machZ: + MOVW R0, (R4) + ADD $4, R4 + CMP.S $PADDR(L1+L1X(0)), R4 /* end at top-level page table */ + BNE _machZ + + /* + * set up the MMU page table + */ + +WAVE('n') + /* clear all PTEs first, to provide a default */ +// MOVW $PADDR(L1+L1X(0)), R4 /* address of PTE for 0 */ +_ptenv0: + ZEROPTE() + CMP.S $PADDR(L1+16*KiB), R4 + BNE _ptenv0 + + DELAY(printloop4, 2) +WAVE(' ') + /* + * set up double map of PHYSDRAM, KZERO to PHYSDRAM for first few MBs, + * but only if KZERO and PHYSDRAM differ. + */ + MOVW $PTEDRAM, R2 /* PTE bits */ + MOVW $PHYSDRAM, R3 /* pa */ + CMP $KZERO, R3 + BEQ no2map + MOVW $PADDR(L1+L1X(PHYSDRAM)), R4 /* address of PTE for PHYSDRAM */ + MOVW $DOUBLEMAPMBS, R5 +_ptdbl: + FILLPTE() + SUB.S $1, R5 + BNE _ptdbl +no2map: + + /* + * back up and fill in PTEs for memory at KZERO. + * beagle has 1 bank of 256MB of SDRAM at PHYSDRAM; + * igepv2 has 1 bank of 512MB at PHYSDRAM. + * Map the maximum (512MB). + */ +WAVE('9') + MOVW $PTEDRAM, R2 /* PTE bits */ + MOVW $PHYSDRAM, R3 + MOVW $PADDR(L1+L1X(KZERO)), R4 /* start with PTE for KZERO */ + MOVW $512, R5 /* inner loop count (MBs) */ +_ptekrw: /* set PTEs */ + FILLPTE() + SUB.S $1, R5 /* decrement inner loop count */ + BNE _ptekrw + + /* + * back up and fill in PTEs for MMIO + * stop somewhere after uarts + */ +WAVE(' ') + MOVW $PTEIO, R2 /* PTE bits */ + MOVW $PHYSIO, R3 + MOVW $PADDR(L1+L1X(VIRTIO)), R4 /* start with PTE for VIRTIO */ +_ptenv2: + FILLPTE() + CMP.S $PADDR(L1+L1X(PHYSIOEND)), R4 + BNE _ptenv2 + + /* mmu.c sets up the trap vectors later */ + + /* + * set up a temporary stack; avoid data & bss segments + */ + MOVW $(PHYSDRAM | (128*1024*1024)), R13 + + /* invalidate caches */ + BL cachedinv(SB) + MOVW $KZERO, R0 + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall + ISB + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait + BARRIERS + +WAVE('f') + /* + * turn caches on + */ + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + ORR $CpACl2en, R1 /* turn l2 cache on */ + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + BARRIERS + + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + ORR $(CpCdcache|CpCicache), R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl + BARRIERS + +WAVE('r') + /* set the domain access control */ + MOVW $Client, R0 + BL dacput(SB) + + DELAY(printloop5, 2) +WAVE('o') + /* set the translation table base */ + MOVW $PADDR(L1), R0 + BL ttbput(SB) + + MOVW $0, R0 + BL pidput(SB) /* paranoia */ + +WAVE('m') + /* + * the little dance to turn the MMU on + */ + BL cacheuwbinv(SB) + BL mmuinvalidate(SB) + BL mmuenable(SB) + +WAVE(' ') + /* warp the PC into the virtual map */ + MOVW $KZERO, R0 + BL _r15warp(SB) + + /* + * now running at KZERO+something! + */ + + MOVW $setR12(SB), R12 /* reload the SB */ + + /* + * set up temporary stack again, in case we've just switched + * to a new register set. + */ + MOVW $(KZERO|(128*1024*1024)), R13 + + /* can now execute arbitrary C code */ + + BL cacheuwbinv(SB) + +WAVE('B') + MOVW $PHYSDRAM, R3 /* pa */ + CMP $KZERO, R3 + BEQ no2unmap + /* undo double map of PHYSDRAM, KZERO & first few MBs */ + MOVW $(L1+L1X(PHYSDRAM)), R4 /* addr. of PTE for PHYSDRAM */ + MOVW $0, R0 + MOVW $DOUBLEMAPMBS, R5 +_ptudbl: + ZEROPTE() + SUB.S $1, R5 + BNE _ptudbl +no2unmap: + BARRIERS + MOVW $KZERO, R0 + MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv + BARRIERS + +#ifdef HIGH_SECURITY /* i.e., not GP omap */ + /* hack: set `secure monitor' vector base addr for cortex */ +// MOVW $HVECTORS, R0 + MOVW $PADDR(L1), R0 + SUB $(MACHSIZE+(2*1024)), R0 + MCR CpSC, 0, R0, C(CpVECS), C(CpVECSbase), CpVECSmon + ISB +#endif + + /* + * call main in C + * pass Mach to main and set up the stack in it + */ + MOVW $(MACHADDR), R0 /* Mach */ + MOVW R0, R13 + ADD $(MACHSIZE), R13 /* stack pointer */ + SUB $4, R13 /* space for link register */ + MOVW R0, R10 /* m = MACHADDR */ +WAVE('e') + BL main(SB) /* void main(Mach*) */ + /*FALLTHROUGH*/ + +/* + * reset the system + */ + +TEXT _reset(SB), 1, $-4 + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R0 + MOVW R0, CPSR + BARRIERS + + DELAY(printloopr, 2) +WAVE('!') +WAVE('r') +WAVE('e') +WAVE('s') +WAVE('e') +WAVE('t') +WAVE('!') +WAVE('\r') +WAVE('\n') + + /* turn the caches off */ + BL cacheuwbinv(SB) + + MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl + BIC $(CpCicache|CpCdcache|CpCalign), R0 + ORR $CpCsw, R0 /* enable SWP */ + MCR CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl + BARRIERS + + /* redo double map of PHYSDRAM, KZERO & first few MBs */ + MOVW $PTEDRAM, R2 /* PTE bits */ + MOVW $PHYSDRAM, R3 /* pa */ + MOVW $(L1+L1X(PHYSDRAM)), R4 /* address of PHYSDRAM's PTE */ + MOVW $DOUBLEMAPMBS, R5 +_ptrdbl: + FILLPTE() + SUB.S $1, R5 + BNE _ptrdbl + + MOVW $PHYSDRAM, R0 + MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv + BARRIERS + + /* turn the MMU off */ + MOVW $PHYSDRAM, R0 + BL _r15warp(SB) + BL mmuinvalidate(SB) + BL mmudisable(SB) + + /* set new reset vector */ + MOVW $HVECTORS, R2 + MOVW $0xe59ff018, R3 /* MOVW 0x18(R15), R15 */ + MOVW R3, (R2) + BARRIERS + +// MOVW $PHYSFLASH, R3 /* TODO */ +// MOVW R3, 0x20(R2) /* where $0xe59ff018 jumps to */ + + /* ...and jump to it */ +// MOVW R2, R15 /* software reboot */ +_limbo: /* should not get here... */ + BL idlehands(SB) + B _limbo /* ... and can't get out */ + BL _div(SB) /* hack to load _div, etc. */ + +TEXT _r15warp(SB), 1, $-4 + BIC $KSEGM, R14 /* link reg, will become PC */ + ORR R0, R14 + BIC $KSEGM, R13 /* SP too */ + ORR R0, R13 + RET + +/* + * `single-element' cache operations. + * in arm arch v7, 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 + +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 + /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */ + MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait + ISB + + 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 + +/* + * enable mmu and high vectors + */ +TEXT mmuenable(SB), 1, $-4 + MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl + ORR $(CpChv|CpCmmu), R0 + MCR CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl + BARRIERS + RET + +TEXT mmudisable(SB), 1, $-4 + MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl + BIC $(CpChv|CpCmmu), R0 + MCR CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl + BARRIERS + RET + +/* + * If one of these MCR instructions crashes or hangs the machine, + * check your Level 1 page table (at TTB) closely. + */ +TEXT mmuinvalidate(SB), $-4 /* invalidate all */ + MOVW CPSR, R2 + CPSID /* interrupts off */ + + BARRIERS + MOVW PC, R0 /* some valid virtual address */ + MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv + BARRIERS + MOVW R2, CPSR /* interrupts restored */ + RET + +TEXT mmuinvalidateaddr(SB), $-4 /* invalidate single entry */ + MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinvse + BARRIERS + RET + +TEXT cpidget(SB), 1, $-4 /* main ID */ + MRC CpSC, 0, R0, C(CpID), C(0), CpIDid + RET + +TEXT cpctget(SB), 1, $-4 /* cache type */ + MRC CpSC, 0, R0, C(CpID), C(0), CpIDct + RET + +TEXT controlget(SB), 1, $-4 /* control */ + MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl + RET + +TEXT ttbget(SB), 1, $-4 /* translation table base */ + MRC CpSC, 0, R0, C(CpTTB), C(0), CpTTB0 + RET + +TEXT ttbput(SB), 1, $-4 /* translation table base */ + MCR CpSC, 0, R0, C(CpTTB), C(0), CpTTB0 + MCR CpSC, 0, R0, C(CpTTB), C(0), CpTTB1 /* cortex has two */ + ISB + RET + +TEXT dacget(SB), 1, $-4 /* domain access control */ + MRC CpSC, 0, R0, C(CpDAC), C(0) + RET + +TEXT dacput(SB), 1, $-4 /* domain access control */ + MCR CpSC, 0, R0, C(CpDAC), C(0) + ISB + RET + +TEXT fsrget(SB), 1, $-4 /* fault status */ + MRC CpSC, 0, R0, C(CpFSR), C(0) + RET + +TEXT farget(SB), 1, $-4 /* fault address */ + MRC CpSC, 0, R0, C(CpFAR), C(0x0) + RET + +TEXT getpsr(SB), 1, $-4 + MOVW CPSR, R0 + RET + +TEXT getscr(SB), 1, $-4 + MRC CpSC, 0, R0, C(CpCONTROL), C(CpCONTROLscr), CpSCRscr + RET + +TEXT pidget(SB), 1, $-4 /* address translation pid */ + MRC CpSC, 0, R0, C(CpPID), C(0x0) + RET + +TEXT pidput(SB), 1, $-4 /* address translation pid */ + MCR CpSC, 0, R0, C(CpPID), C(0x0) + ISB + RET + +TEXT splhi(SB), 1, $-4 + MOVW CPSR, R0 + CPSID /* turn off interrupts */ + + MOVW $(MACHADDR+4), R2 /* save caller pc in Mach */ + MOVW R14, 0(R2) + RET + +TEXT spllo(SB), 1, $-4 /* start marker for devkprof.c */ + MOVW CPSR, R0 + CPSIE + RET + +TEXT splx(SB), 1, $-4 + MOVW $(MACHADDR+0x04), R2 /* save caller pc in Mach */ + MOVW R14, 0(R2) + + MOVW CPSR, R3 + MOVW R0, CPSR /* reset interrupt level */ + MOVW R3, R0 /* must return old 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 armv7 */ + RET + +TEXT clz(SB), $-4 + CLZ(0, 0) /* 0 is R0 */ + 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 + WFI + RET + +TEXT coherence(SB), $-4 + BARRIERS + RET + +#include "cache.v7.s" diff --git a/sys/src/9/omap/lexception.s b/sys/src/9/omap/lexception.s new file mode 100755 index 000000000..e3f330653 --- /dev/null +++ b/sys/src/9/omap/lexception.s @@ -0,0 +1,187 @@ +/* + * arm exception handlers + */ +#include "arm.s" + +#undef B /* B is for 'botch' */ + +/* + * exception vectors, copied by trapinit() to somewhere useful + */ +TEXT vectors(SB), 1, $-4 + MOVW 0x18(R15), R15 /* reset */ + MOVW 0x18(R15), R15 /* undefined instr. */ + MOVW 0x18(R15), R15 /* SWI & SMC */ + MOVW 0x18(R15), R15 /* prefetch abort */ + MOVW 0x18(R15), R15 /* data abort */ + MOVW 0x18(R15), R15 /* reserved */ + MOVW 0x18(R15), R15 /* IRQ */ + MOVW 0x18(R15), R15 /* FIQ */ + +TEXT vtable(SB), 1, $-4 + WORD $_vsvc(SB) /* reset, in svc mode already */ + WORD $_vund(SB) /* undefined, switch to svc mode */ + WORD $_vsvc(SB) /* swi, in svc mode already */ + WORD $_vpabt(SB) /* prefetch abort, switch to svc mode */ + WORD $_vdabt(SB) /* data abort, switch to svc mode */ + WORD $_vsvc(SB) /* reserved */ + WORD $_virq(SB) /* IRQ, switch to svc mode */ +// WORD $_vfiq(SB) /* FIQ, switch to svc mode */ + WORD $_virq(SB) /* FIQ, switch to svc mode */ + +TEXT _vrst(SB), 1, $-4 + BL _reset(SB) + +TEXT _vsvc(SB), 1, $-4 /* SWI */ + MOVW.W R14, -4(R13) /* ureg->pc = interrupted PC */ + MOVW SPSR, R14 /* ureg->psr = SPSR */ + MOVW.W R14, -4(R13) /* ... */ + MOVW $PsrMsvc, R14 /* ureg->type = PsrMsvc */ + MOVW.W R14, -4(R13) /* ... */ + + /* avoid the ambiguity described in notes/movm.w. */ +// MOVM.DB.W.S [R0-R14], (R13) /* save user level registers, at end r13 points to ureg */ + MOVM.DB.S [R0-R14], (R13) /* save user level registers */ + SUB $(15*4), R13 /* r13 now points to ureg */ + + MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */ + +// MOVW $(KSEG0+16*KiB-MACHSIZE), R10 /* m */ + MOVW $(L1-MACHSIZE), R10 /* m */ + MOVW 8(R10), R9 /* up */ + + MOVW R13, R0 /* first arg is pointer to ureg */ + SUB $8, R13 /* space for argument+link */ + + BL syscall(SB) + + ADD $(8+4*15), R13 /* make r13 point to ureg->type */ + MOVW 8(R13), R14 /* restore link */ + MOVW 4(R13), R0 /* restore SPSR */ + MOVW R0, SPSR /* ... */ + MOVM.DB.S (R13), [R0-R14] /* restore registers */ + ADD $8, R13 /* pop past ureg->{type+psr} */ + RFE /* MOVM.IA.S.W (R13), [R15] */ + +TEXT _vund(SB), 1, $-4 /* undefined */ + MOVM.IA [R0-R4], (R13) /* free some working space */ + MOVW $PsrMund, R0 + B _vswitch + +TEXT _vpabt(SB), 1, $-4 /* prefetch abort */ + MOVM.IA [R0-R4], (R13) /* free some working space */ + MOVW $PsrMabt, R0 /* r0 = type */ + B _vswitch + +TEXT _vdabt(SB), 1, $-4 /* data abort */ + MOVM.IA [R0-R4], (R13) /* free some working space */ + MOVW $(PsrMabt+1), R0 /* r0 = type */ + B _vswitch + +TEXT _virq(SB), 1, $-4 /* IRQ */ + MOVM.IA [R0-R4], (R13) /* free some working space */ + MOVW $PsrMirq, R0 /* r0 = type */ + B _vswitch + + /* + * come here with type in R0 and R13 pointing above saved [r0-r4]. + * we'll switch to SVC mode and then call trap. + */ +_vswitch: + MOVW SPSR, R1 /* save SPSR for ureg */ + MOVW R14, R2 /* save interrupted pc for ureg */ + MOVW R13, R3 /* save pointer to where the original [R0-R4] are */ + + /* + * switch processor to svc mode. this switches the banked registers + * (r13 [sp] and r14 [link]) to those of svc mode. + */ + MOVW CPSR, R14 + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR /* switch! */ + + AND.S $0xf, R1, R4 /* interrupted code kernel or user? */ + BEQ _userexcep + + /* here for trap from SVC mode */ + MOVM.DB.W [R0-R2], (R13) /* set ureg->{type, psr, pc}; r13 points to ureg->type */ + MOVM.IA (R3), [R0-R4] /* restore [R0-R4] from previous mode's stack */ + + /* + * avoid the ambiguity described in notes/movm.w. + * In order to get a predictable value in R13 after the stores, + * separate the store-multiple from the stack-pointer adjustment. + * We'll assume that the old value of R13 should be stored on the stack. + */ + /* save kernel level registers, at end r13 points to ureg */ +// MOVM.DB.W [R0-R14], (R13) + MOVM.DB [R0-R14], (R13) + SUB $(15*4), R13 /* SP now points to saved R0 */ + + MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */ + + MOVW R13, R0 /* first arg is pointer to ureg */ + SUB $(4*2), R13 /* space for argument+link (for debugger) */ + MOVW $0xdeaddead, R11 /* marker */ + + BL trap(SB) + + ADD $(4*2+4*15), R13 /* make r13 point to ureg->type */ + MOVW 8(R13), R14 /* restore link */ + MOVW 4(R13), R0 /* restore SPSR */ + MOVW R0, SPSR /* ... */ + + MOVM.DB (R13), [R0-R14] /* restore registers */ + + ADD $(4*2), R13 /* pop past ureg->{type+psr} to pc */ + RFE /* MOVM.IA.S.W (R13), [R15] */ + + /* here for trap from USER mode */ +_userexcep: + MOVM.DB.W [R0-R2], (R13) /* set ureg->{type, psr, pc}; r13 points to ureg->type */ + MOVM.IA (R3), [R0-R4] /* restore [R0-R4] from previous mode's stack */ + + /* avoid the ambiguity described in notes/movm.w. */ +// MOVM.DB.W.S [R0-R14], (R13) /* save kernel level registers, at end r13 points to ureg */ + MOVM.DB.S [R0-R14], (R13) /* save kernel level registers */ + SUB $(15*4), R13 /* r13 now points to ureg */ + + MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */ + +// MOVW $(KSEG0+16*KiB-MACHSIZE), R10 /* m */ + MOVW $(L1-MACHSIZE), R10 /* m */ + MOVW 8(R10), R9 /* up */ + + MOVW R13, R0 /* first arg is pointer to ureg */ + SUB $(4*2), R13 /* space for argument+link (for debugger) */ + + BL trap(SB) + + ADD $(4*2+4*15), R13 /* make r13 point to ureg->type */ + MOVW 8(R13), R14 /* restore link */ + MOVW 4(R13), R0 /* restore SPSR */ + MOVW R0, SPSR /* ... */ + MOVM.DB.S (R13), [R0-R14] /* restore registers */ + ADD $(4*2), R13 /* pop past ureg->{type+psr} */ + RFE /* MOVM.IA.S.W (R13), [R15] */ + +TEXT _vfiq(SB), 1, $-4 /* FIQ */ + RFE /* FIQ is special, ignore it for now */ + +/* + * set the stack value for the mode passed in R0 + */ +TEXT setr13(SB), 1, $-4 + MOVW 4(FP), R1 + + MOVW CPSR, R2 + BIC $PsrMask, R2, R3 + ORR R0, R3 + MOVW R3, CPSR /* switch to new mode */ + + MOVW R13, R0 /* return old sp */ + MOVW R1, R13 /* install new one */ + + MOVW R2, CPSR /* switch back to old mode */ + RET diff --git a/sys/src/9/omap/lproc.s b/sys/src/9/omap/lproc.s new file mode 100755 index 000000000..0b4eac238 --- /dev/null +++ b/sys/src/9/omap/lproc.s @@ -0,0 +1,47 @@ +#include "mem.h" +#include "arm.h" + +/* + * This is the first jump from kernel to user mode. + * Fake a return from interrupt. + * + * Enter with R0 containing the user stack pointer. + * UTZERO + 0x20 is always the entry point. + * + */ +TEXT touser(SB), 1, $-4 + /* store the user stack pointer into the USR_r13 */ + MOVM.DB.W [R0], (R13) + /* avoid the ambiguity described in notes/movm.w. */ +// MOVM.S.IA.W (R13), [R13] + MOVM.S (R13), [R13] + ADD $4, R13 + + /* set up a PSR for user level */ + MOVW $(PsrMusr), R0 + MOVW R0, SPSR + + /* save the PC on the stack */ + MOVW $(UTZERO+0x20), R0 + MOVM.DB.W [R0], (R13) + + /* + * note that 5a's RFE is not the v6 arch. instruction (0xe89d0a00, + * I think), which loads CPSR from the word after the PC at (R13), + * but rather the pre-v6 simulation `MOVM.IA.S.W (R13), [R15]' + * (0xe8fd8000 since MOVM is LDM in this case), which loads CPSR + * not from memory but from SPSR due to `.S'. + */ + RFE + +/* + * here to jump to a newly forked process + */ +TEXT forkret(SB), 1, $-4 + ADD $(4*15), R13 /* make r13 point to ureg->type */ + MOVW 8(R13), R14 /* restore link */ + MOVW 4(R13), R0 /* restore SPSR */ + MOVW R0, SPSR /* ... */ + MOVM.DB.S (R13), [R0-R14] /* restore registers */ + ADD $8, R13 /* pop past ureg->{type+psr} */ + RFE /* MOVM.IA.S.W (R13), [R15] */ diff --git a/sys/src/9/omap/main.c b/sys/src/9/omap/main.c new file mode 100755 index 000000000..6f04f765a --- /dev/null +++ b/sys/src/9/omap/main.c @@ -0,0 +1,698 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "init.h" +#include + +#include "reboot.h" + +/* + * Where configuration info is left for the loaded programme. + * This will turn into a structure as more is done by the boot loader + * (e.g. why parse the .ini file twice?). + * There are 3584 bytes available at CONFADDR. + */ +#define BOOTARGS ((char*)CONFADDR) +#define BOOTARGSLEN (16*KiB) /* limit in devenv.c */ +#define MAXCONF 64 +#define MAXCONFLINE 160 + +enum { + Minmem = 256*MB, /* conservative default */ +}; + +#define isascii(c) ((uchar)(c) > 0 && (uchar)(c) < 0177) + +uintptr kseg0 = KZERO; +Mach* machaddr[MAXMACH]; + +/* + * Option arguments from the command line. + * oargv[0] is the boot file. + * Optionsinit() is called from multiboot() + * or some other machine-dependent place + * to set it all up. + */ +static int oargc; +static char* oargv[20]; +static char oargb[128]; +static int oargblen; +static char oenv[4096]; + +static uintptr sp; /* XXX - must go - user stack of init proc */ + +int vflag; +int normalprint; +char debug[256]; + +/* store plan9.ini contents here at least until we stash them in #ec */ +static char confname[MAXCONF][KNAMELEN]; +static char confval[MAXCONF][MAXCONFLINE]; +static int nconf; + +static int +findconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return i; + return -1; +} + +char* +getconf(char *name) +{ + int i; + + i = findconf(name); + if(i >= 0) + return confval[i]; + return nil; +} + +void +addconf(char *name, char *val) +{ + int i; + + i = findconf(name); + if(i < 0){ + if(val == nil || nconf >= MAXCONF) + return; + i = nconf++; + strecpy(confname[i], confname[i]+sizeof(confname[i]), name); + } +// confval[i] = val; + strecpy(confval[i], confval[i]+sizeof(confval[i]), val); +} + +static void +writeconf(void) +{ + char *p, *q; + int n; + + p = getconfenv(); + + if(waserror()) { + free(p); + nexterror(); + } + + /* convert to name=value\n format */ + for(q=p; *q; q++) { + q += strlen(q); + *q = '='; + q += strlen(q); + *q = '\n'; + } + n = q - p + 1; + if(n >= BOOTARGSLEN) + error("kernel configuration too large"); + memmove(BOOTARGS, p, n); + memset(BOOTARGS + n, '\n', BOOTARGSLEN - n); + poperror(); + free(p); +} + +/* + * assumes that we have loaded our /cfg/pxe/mac file at 0x1000 with + * tftp in u-boot. no longer uses malloc, so can be called early. + */ +static void +plan9iniinit(void) +{ + char *k, *v, *next; + + k = (char *)CONFADDR; + if(!isascii(*k)) + return; + + for(; k && *k != '\0'; k = next) { + if (!isascii(*k)) /* sanity check */ + break; + next = strchr(k, '\n'); + if (next) + *next++ = '\0'; + + if (*k == '\0' || *k == '\n' || *k == '#') + continue; + v = strchr(k, '='); + if(v == nil) + continue; /* mal-formed line */ + *v++ = '\0'; + + addconf(k, v); + } +} + +static void +optionsinit(char* s) +{ + char *o; + + strcpy(oenv, ""); + o = strecpy(oargb, oargb+sizeof(oargb), s)+1; + if(getenv("bootargs", o, o - oargb) != nil) + *(o-1) = ' '; + + oargblen = strlen(oargb); + oargc = tokenize(oargb, oargv, nelem(oargv)-1); + oargv[oargc] = nil; +} + +char* +getenv(char* name, char* buf, int n) +{ + char *e, *p, *q; + + p = oenv; + while(*p != 0){ + if((e = strchr(p, '=')) == nil) + break; + for(q = name; p < e; p++){ + if(*p != *q) + break; + q++; + } + if(p == e && *q == 0){ + strecpy(buf, buf+n, e+1); + return buf; + } + p += strlen(p)+1; + } + + return nil; +} + +void +main(void) +{ +// int i; + extern char bdata[], edata[], end[], etext[]; + static ulong vfy = 0xcafebabe; + + /* l.s has already printed "Plan 9 from Be" */ +// m = mach; /* now done in l.s */ + + /* realign data seg; apparently -H0 -R4096 does not pad the text seg */ + if (vfy != 0xcafebabe) { +// wave('<'); wave('-'); + memmove(bdata, etext, edata - bdata); + } + /* + * once data segment is in place, always zero bss since we may + * have been loaded by another Plan 9 kernel. + */ + memset(edata, 0, end - edata); /* zero BSS */ + cacheuwbinv(); + l2cacheuwbinv(); + + if (vfy != 0xcafebabe) + panic("data segment misaligned"); + vfy = 0; + +wave('l'); + machinit(); + mmuinit(); + + optionsinit("/boot/boot boot"); + quotefmtinstall(); + + /* want plan9.ini to be able to affect memory sizing in confinit */ + plan9iniinit(); /* before we step on plan9.ini in low memory */ + + trapinit(); /* so confinit can probe memory to size it */ + confinit(); /* figures out amount of memory */ + /* xinit prints (if it can), so finish up the banner here. */ + delay(500); + iprint("l Labs\n\n"); + delay(500); + xinit(); + + mainmem->flags |= POOL_ANTAGONISM /* | POOL_PARANOIA */ ; + + /* + * Printinit will cause the first malloc call. + * (printinit->qopen->malloc) unless any of the + * above (like clockinit) do an irqenable, which + * will call malloc. + * If the system dies here it's probably due + * to malloc(->xalloc) not being initialised + * correctly, or the data segment is misaligned + * (it's amazing how far you can get with + * things like that completely broken). + * + * (Should be) boilerplate from here on. + */ + + archreset(); /* configure clock signals */ + clockinit(); /* start clocks */ + timersinit(); + watchdoginit(); + + delay(250); /* let uart catch up */ + printinit(); + kbdenable(); + + cpuidprint(); +// chkmissing(); + + procinit0(); + initseg(); + + dmainit(); + links(); + conf.monitor = 1; + screeninit(); + chandevreset(); /* most devices are discovered here */ + +// i8250console(); /* too early; see init0 */ + + pageinit(); + swapinit(); + userinit(); + schedinit(); +} + +void +machinit(void) +{ + if (m == 0) + wave('?'); +// memset(m, 0, sizeof(Mach)); /* done by l.s, now contains stack */ + m->machno = 0; + machaddr[m->machno] = m; + + m->ticks = 1; + m->perf.period = 1; + + conf.nmach = 1; + + active.machs = 1; + active.exiting = 0; + + up = nil; +} + +static void +shutdown(int ispanic) +{ + int ms, once; + + lock(&active); + if(ispanic) + active.ispanic = ispanic; + else if(m->machno == 0 && (active.machs & (1<machno)) == 0) + active.ispanic = 0; + once = active.machs & (1<machno); + active.machs &= ~(1<machno); + active.exiting = 1; + unlock(&active); + + if(once) + iprint("cpu%d: exiting\n", m->machno); + spllo(); + for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){ + delay(TK2MS(2)); + if(active.machs == 0 && consactive() == 0) + break; + } + delay(1000); +} + +/* + * exit kernel either on a panic or user request + */ +void +exit(int code) +{ + shutdown(code); + splhi(); + archreboot(); +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[32], *p; + int i; + + snprint(cc, sizeof cc, "%s%d", class, ctlrno); + p = getconf(cc); + if(p == nil) + return 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; + else if(cistrncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "dma=", 4) == 0) + isa->dma = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "freq=", 5) == 0) + isa->freq = strtoul(p+5, &p, 0); + } + return 1; +} + +/* + * the new kernel is already loaded at address `code' + * of size `size' and entry point `entry'. + */ +void +reboot(void *entry, void *code, ulong size) +{ + void (*f)(ulong, ulong, ulong); + + print("starting reboot..."); + writeconf(); + shutdown(0); + + /* + * should be the only processor running now + */ + + print("reboot entry %#lux code %#lux size %ld\n", + PADDR(entry), PADDR(code), size); + delay(100); + + /* turn off buffered serial console */ + serialoq = nil; + kprintoq = nil; + screenputs = nil; + + /* shutdown devices */ + chandevshutdown(); + + /* call off the dog */ + clockshutdown(); + + splhi(); + intrsoff(); + + /* setup reboot trampoline function */ + f = (void*)REBOOTADDR; + memmove(f, rebootcode, sizeof(rebootcode)); + cacheuwbinv(); + l2cacheuwbinv(); + + /* off we go - never to return */ + (*f)(PADDR(entry), PADDR(code), size); + + iprint("loaded kernel returned!\n"); + delay(1000); + archreboot(); +} + +/* + * starting place for first process + */ +void +init0(void) +{ + int i; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + coherence(); + spllo(); + + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + up->slash = namec("#/", Atodir, 0, 0); + pathclose(up->slash->path); + up->slash->path = newpath("/"); + up->dot = cclone(up->slash); + + dmatest(); /* needs `up' set, so can't do it earlier */ + chandevinit(); + i8250console(); /* might be redundant, but harmless */ + if(kbdq == nil) + panic("init0: nil kbdq"); + if(serialoq == nil) + panic("init0: nil serialoq"); + normalprint = 1; + + if(!waserror()){ + snprint(buf, sizeof(buf), "%s %s", "ARM", conffile); + ksetenv("terminal", buf, 0); + ksetenv("cputype", "arm", 0); + if(cpuserver) + ksetenv("service", "cpu", 0); + else + ksetenv("service", "terminal", 0); + + /* convert plan9.ini variables to #e and #ec */ + for(i = 0; i < nconf; i++) { + ksetenv(confname[i], confval[i], 0); + ksetenv(confname[i], confval[i], 1); + } + poperror(); + } + kproc("alarm", alarmkproc, 0); + touser(sp); +} + +static void +bootargs(uintptr base) +{ + int i; + ulong ssize; + char **av, *p; + + /* + * Push the boot args onto the stack. + * The initial value of the user stack must be such + * that the total used is larger than the maximum size + * of the argument list checked in syscall. + */ + i = oargblen+1; + p = UINT2PTR(STACKALIGN(base + BY2PG - sizeof(up->s.args) - i)); + memmove(p, oargb, i); + + /* + * Now push argc and the argv pointers. + * This isn't strictly correct as the code jumped to by + * touser in init9.s calls startboot (port/initcode.c) which + * expects arguments + * startboot(char *argv0, char **argv) + * not the usual (int argc, char* argv[]), but argv0 is + * unused so it doesn't matter (at the moment...). + */ + av = (char**)(p - (oargc+2)*sizeof(char*)); + ssize = base + BY2PG - PTR2UINT(av); + *av++ = (char*)oargc; + for(i = 0; i < oargc; i++) + *av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BY2PG); + *av = nil; + + /* + * Leave space for the return PC of the + * caller of initcode. + */ + sp = USTKTOP - ssize - sizeof(void*); +} + +/* + * create the first process + */ +void +userinit(void) +{ + Proc *p; + Segment *s; + KMap *k; + Page *pg; + + /* no processes yet */ + up = nil; + + p = newproc(); + p->pgrp = newpgrp(); + p->egrp = smalloc(sizeof(Egrp)); + p->egrp->ref = 1; + p->fgrp = dupfgrp(nil); + p->rgrp = newrgrp(); + p->procmode = 0640; + + kstrdup(&eve, ""); + kstrdup(&p->text, "*init*"); + kstrdup(&p->user, eve); + + /* + * Kernel Stack + */ + p->sched.pc = PTR2UINT(init0); + p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->s.args)-sizeof(uintptr)); + p->sched.sp = STACKALIGN(p->sched.sp); + + /* + * User Stack + * + * Technically, newpage can't be called here because it + * should only be called when in a user context as it may + * try to sleep if there are no pages available, but that + * shouldn't be the case here. + */ + s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); + s->flushme++; + p->seg[SSEG] = s; + pg = newpage(1, 0, USTKTOP-BY2PG); + segpage(s, pg); + k = kmap(pg); + bootargs(VA(k)); + kunmap(k); + + /* + * Text + */ + s = newseg(SG_TEXT, UTZERO, 1); + p->seg[TSEG] = s; + pg = newpage(1, 0, UTZERO); + memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); + segpage(s, pg); + k = kmap(s->map[0]->pages[0]); + memmove(UINT2PTR(VA(k)), initcode, sizeof initcode); + kunmap(k); + + ready(p); +} + +Conf conf; /* XXX - must go - gag */ + +Confmem omapmem[nelem(conf.mem)] = { + /* + * Memory available to Plan 9: + */ + { .base = PHYSDRAM, .limit = PHYSDRAM + Minmem, }, +}; +ulong memsize = Minmem; + +static int +gotmem(uintptr sz) +{ + uintptr addr; + + addr = PHYSDRAM + sz - BY2WD; + mmuidmap(addr, 1); + if (probeaddr(addr) >= 0) { + memsize = sz; + return 0; + } + return -1; +} + +void +confinit(void) +{ + int i; + ulong kpages; + uintptr pa; + char *p; + + /* + * Copy the physical memory configuration to Conf.mem. + */ + if(nelem(omapmem) > nelem(conf.mem)){ + iprint("memory configuration botch\n"); + exit(1); + } + if((p = getconf("*maxmem")) != nil) { + memsize = strtoul(p, 0, 0) - PHYSDRAM; + if (memsize < 16*MB) /* sanity */ + memsize = 16*MB; + } + + /* + * see if all that memory exists; if not, find out how much does. + * trapinit must have been called first. + */ + if (gotmem(memsize) < 0 && gotmem(256*MB) < 0 && gotmem(128*MB) < 0) { + iprint("can't find any memory, assuming %dMB\n", Minmem / MB); + memsize = Minmem; + } + + omapmem[0].limit = PHYSDRAM + memsize; + memmove(conf.mem, omapmem, sizeof(omapmem)); + + conf.npage = 0; + pa = PADDR(PGROUND(PTR2UINT(end))); + + /* + * we assume that the kernel is at the beginning of one of the + * contiguous chunks of memory and fits therein. + */ + for(i=0; i conf.mem[i].base && pa < conf.mem[i].limit) + conf.mem[i].base = pa; + + conf.mem[i].npage = (conf.mem[i].limit - conf.mem[i].base)/BY2PG; + conf.npage += conf.mem[i].npage; + } + + conf.upages = (conf.npage*80)/100; + conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG; + + /* only one processor */ + conf.nmach = 1; + + /* set up other configuration parameters */ + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + if(cpuserver) + conf.nproc *= 3; + if(conf.nproc > 2000) + conf.nproc = 2000; + conf.nswap = conf.npage*3; + conf.nswppo = 4096; + conf.nimage = 200; + + conf.copymode = 0; /* copy on write */ + + /* + * Guess how much is taken by the large permanent + * datastructures. Mntcache and Mntrpc are not accounted for + * (probably ~300KB). + */ + kpages = conf.npage - conf.upages; + kpages *= BY2PG; + kpages -= conf.upages*sizeof(Page) + + conf.nproc*sizeof(Proc) + + conf.nimage*sizeof(Image) + + conf.nswap + + conf.nswppo*sizeof(Page); + mainmem->maxsize = kpages; + if(!cpuserver) + /* + * give terminals lots of image memory, too; the dynamic + * allocation will balance the load properly, hopefully. + * be careful with 32-bit overflow. + */ + imagmem->maxsize = kpages; + +// archconfinit(); +} + +int +cmpswap(long *addr, long old, long new) +{ + return cas32(addr, old, new); +} diff --git a/sys/src/9/omap/mem.h b/sys/src/9/omap/mem.h new file mode 100755 index 000000000..1e5c570c9 --- /dev/null +++ b/sys/src/9/omap/mem.h @@ -0,0 +1,212 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ +#define KiB 1024u /* Kibi 0x0000000000000400 */ +#define MiB 1048576u /* Mebi 0x0000000000100000 */ +#define GiB 1073741824u /* Gibi 000000000040000000 */ + +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) /* ceiling */ +#define ROUNDDN(x, y) (((x)/(y))*(y)) /* floor */ +#define MIN(a, b) ((a) < (b)? (a): (b)) +#define MAX(a, b) ((a) > (b)? (a): (b)) + +/* + * Not sure where these macros should go. + * This probably isn't right but will do for now. + * The macro names are problematic too. + */ +/* + * In B(o), 'o' is the bit offset in the register. + * For multi-bit fields use F(v, o, w) where 'v' is the value + * of the bit-field of width 'w' with LSb at bit offset 'o'. + */ +#define B(o) (1<<(o)) +#define F(v, o, w) (((v) & ((1<<(w))-1))<<(o)) + +#define FCLR(d, o, w) ((d) & ~(((1<<(w))-1)<<(o))) +#define FEXT(d, o, w) (((d)>>(o)) & ((1<<(w))-1)) +#define FINS(d, o, w, v) (FCLR((d), (o), (w))|F((v), (o), (w))) +#define FSET(d, o, w) ((d)|(((1<<(w))-1)<<(o))) + +#define FMASK(o, w) (((1<<(w))-1)<<(o)) + +/* + * Sizes + */ +#define BY2PG (4*KiB) /* bytes per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define PGROUND(s) ROUNDUP(s, BY2PG) +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) + +#define MAXMACH 1 /* max # cpus system can run */ +#define MACHSIZE BY2PG + +#define KSTKSIZE (16*KiB) /* was 8K */ +#define STACKALIGN(sp) ((sp) & ~3) /* bug: assure with alloc */ + +/* + * Address spaces. + * KTZERO is used by kprof and dumpstack (if any). + * + * KZERO (0xc0000000) is mapped to physical 0x80000000 (start of dram). + * u-boot claims to occupy the first 3 MB of dram, but we're willing to + * step on it once we're loaded. Expect plan9.ini in the first 64K past 3MB. + * + * L2 PTEs are stored in 1K before Mach (11K to 12K above KZERO). + * cpu0's Mach struct is at L1 - MACHSIZE(4K) to L1 (12K to 16K above KZERO). + * L1 PTEs are stored from L1 to L1+32K (16K to 48K above KZERO). + * KTZERO may be anywhere after that (but probably shouldn't collide with + * u-boot). + * This should leave over 8K from KZERO to L2 PTEs. + */ +#define KSEG0 0xC0000000 /* kernel segment */ +/* mask to check segment; good for 512MB dram */ +#define KSEGM 0xE0000000 +#define KZERO KSEG0 /* kernel address space */ +#define L1 (KZERO+16*KiB) /* tt ptes: 16KiB aligned */ +#define CONFADDR (KZERO+0x300000) /* unparsed plan9.ini */ +/* KTZERO must match loadaddr in mkfile */ +#define KTZERO (KZERO+0x310000) /* kernel text start */ + +#define UZERO 0 /* user segment */ +#define UTZERO (UZERO+BY2PG) /* user text start */ +#define UTROUND(t) ROUNDUP((t), BY2PG) +/* moved USTKTOP down to 512MB to keep MMIO space out of user space. */ +#define USTKTOP 0x20000000 /* user segment end +1 */ +#define USTKSIZE (8*1024*1024) /* user stack size */ +#define TSTKTOP (USTKTOP-USTKSIZE) /* sysexec temporary stack */ +#define TSTKSIZ 256 + +/* address at which to copy and execute rebootcode */ +#define REBOOTADDR KADDR(0x100) + +/* + * Legacy... + */ +#define BLOCKALIGN 32 /* only used in allocb.c */ +#define KSTACK KSTKSIZE + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BY2SE 4 +#define BY2WD 4 +#define BY2V 8 /* only used in xalloc.c */ + +#define CACHELINESZ 64 /* bytes per cache line */ +#define PTEMAPMEM (1024*1024) +#define PTEPERTAB (PTEMAPMEM/BY2PG) +#define SEGMAPSIZE 1984 /* magic 16*124 */ +#define SSEGMAPSIZE 16 /* magic */ +#define PPN(x) ((x)&~(BY2PG-1)) /* pure page number? */ + +/* + * With a little work these move to port. + */ +#define PTEVALID (1<<0) +#define PTERONLY 0 +#define PTEWRITE (1<<1) +#define PTEUNCACHED (1<<2) +#define PTEKERNEL (1<<3) + +/* + * Physical machine information from here on. + */ + +/* gpmc-controlled address space 0—1G */ +#define PHYSNAND 1 /* cs0 is onenand flash */ +#define PHYSETHER 0x2c000000 + +#define PHYSIO 0x48000000 /* L4 ctl */ + +#define PHYSSCM 0x48002000 /* system control module */ + +/* core control pad cfg 0x48002030—0x480021e4, */ +/* core control d2d pad cfg 0x480021e4—0x48002264 */ +#define PHYSSCMPCONF 0x48002270 /* general device config */ +#define PHYSOMAPSTS 0x4800244c /* standalone short: has l2 size */ +/* core control pad cfg (2) 0x480025d8—0x480025fc */ +#define PHYSSWBOOTCFG 0x48002910 /* sw booting config */ +/* wakeup control pad cfg 0x48002a00—0x48002a54 */ + +#define PHYSSCMMPU 0x48004900 /* actually CPU */ +#define PHYSSCMCORE 0x48004a00 +#define PHYSSCMWKUP 0x48004c00 +#define PHYSSCMPLL 0x48004d00 /* clock ctl for dpll[3-5] */ +#define PHYSSCMDSS 0x48004e00 +#define PHYSSCMPER 0x48005000 +#define PHYSSCMUSB 0x48005400 + +#define PHYSL4CORE 0x48040100 /* l4 ap */ +#define PHYSDSS 0x48050000 /* start of dss registers */ +#define PHYSDISPC 0x48050400 +#define PHYSGFX 0x48050480 /* part of dispc */ + +#define PHYSSDMA 0x48056000 /* system dma */ +#define PHYSDMA 0x48060000 + +#define PHYSUSBTLL 0x48062000 /* usb: transceiver-less link */ +#define PHYSUHH 0x48064000 /* usb: `high-speed usb host' ctlr or subsys */ +#define PHYSOHCI 0x48064400 /* usb 1.0: slow */ +#define PHYSEHCI 0x48064800 /* usb 2.0: medium */ +#define PHYSUART0 0x4806a000 +#define PHYSUART1 0x4806c000 +#define PHYSMMCHS1 0x4809c000 /* mmc/sdio */ +#define PHYSUSBOTG 0x480ab000 /* on-the-go usb */ +#define PHYSMMCHS3 0x480ad000 +#define PHYSMMCHS2 0x480b4000 + +#define PHYSINTC 0x48200000 /* interrupt controller */ + +#define PHYSPRMIVA2 0x48206000 /* prm iva2 regs */ +/* 48306d40 sys_clkin_sel */ +#define PHYSPRMGLBL 0x48307200 /* prm global regs */ +#define PHYSPRMWKUSB 0x48307400 + +#define PHYSCNTRL 0x4830a200 /* SoC id, etc. */ +#define PHYSWDT1 0x4830c000 /* wdt1, not on GP omaps */ + +#define PHYSGPIO1 0x48310000 /* contains dss gpio */ + +#define PHYSWDOG 0x48314000 /* watchdog timer, wdt2 */ +#define PHYSWDT2 0x48314000 /* watchdog timer, wdt2 */ +#define PHYSTIMER1 0x48318000 + +#define PHYSL4WKUP 0x48328100 /* l4 wkup */ +#define PHYSL4PER 0x49000100 /* l4 per */ + +#define PHYSCONS 0x49020000 /* uart console (third one) */ + +#define PHYSWDT3 0x49030000 /* wdt3 */ +#define PHYSTIMER2 0x49032000 +#define PHYSTIMER3 0x49034000 +#define PHYSGPIO5 0x49056000 +#define PHYSGPIO6 0x49058000 /* contains igep ether gpio */ + +#define PHYSIOEND 0x49100000 /* end of PHYSIO identity map */ + +#define PHYSL4EMU 0x54006100 /* l4 emu */ +#define PHYSL4PROT 0x54728000 /* l4 protection regs */ + +#define PHYSL3 0x68000000 /* l3 interconnect control */ +#define PHYSL3GPMCCFG 0x68002000 /* l3 gpmc target port agent cfg */ +#define PHYSL3USB 0x68004000 /* l3 regs for usb */ +#define PHYSL3USBOTG 0x68004400 /* l3 regs for usb otg */ +/* (target port) protection registers */ +#define PHYSL3PMRT 0x68010000 /* l3 PM register target prot. */ +#define PHYSL3GPMCPM 0x68012400 /* l3 gpmc target port protection */ +#define PHYSL3OCTRAM 0x68012800 /* l3 ocm ram */ +#define PHYSL3OCTROM 0x68012c00 /* l3 ocm rom */ +#define PHYSL3MAD2D 0x68013000 /* l3 die-to-die */ +#define PHYSL3IVA 0x68014000 /* l3 die-to-die */ + +#define PHYSSMS 0x6c000000 /* cfg regs: sms addr space 2 */ +#define PHYSDRC 0x6d000000 /* sdram ctlr, addr space 3 */ +#define PHYSGPMC 0x6e000000 /* flash, non-dram memory ctlr */ + +#define PHYSDRAM 0x80000000 + +#define VIRTNAND 0x20000000 /* fixed by u-boot */ +#define VIRTIO PHYSIO diff --git a/sys/src/9/omap/mkfile b/sys/src/9/omap/mkfile new file mode 100755 index 000000000..de353a776 --- /dev/null +++ b/sys/src/9/omap/mkfile @@ -0,0 +1,136 @@ +CONF=beagle +CONFLIST=beagle + +# allegedly u-boot uses the bottom 3MB (up to 0x300000) so avoid that, +# and leave 64K for plan9.ini. loadaddr must match KTZERO in mem.h. +# 0xc0310000 has worked, 0x80310000 should work but doesn't yet. +loadaddr=0xc0310000 + +objtype=arm +$target.list + $LD -o $target -R4096 -T$loadaddr -l $OBJ $CONF.$O $LIB + size $target + +$p$CONF.gz:D: $p$CONF + gzip -9 <$p$CONF >$target + +$OBJ: $HFILES + +install:V: /$objtype/$p$CONF + +/$objtype/$p$CONF:D: $p$CONF s$p$CONF + { cp -x $p$CONF s$p$CONF /$objtype } & + wait + touch $target + +<../boot/bootmkfile +<../port/portmkfile +<|../port/mkbootrules $CONF + +CFLAGS= -I. -I../port $CFLAGS # hack to compile private sysproc.c (e.g.) + +arch.$O clock.$O fpiarm.$O main.$O mmu.$O screen.$O sdscsi.$O syscall.$O \ + trap.$O: /$objtype/include/ureg.h + +archomap.$O devether.$0 ether9221.$O: etherif.h ../port/netif.h +archomap.$O devflash.$O flashbeagle.$O flashigep.$O: ../port/flashif.h +ecc.$O flashbeagle.$O flashigep.$O: ../port/nandecc.h io.h +fpi.$O fpiarm.$O fpimem.$O: fpi.h +l.$O lexception.$O lproc.$O mmu.$O: arm.s arm.h mem.h +l.$O rebootcode.$O: cache.v7.s +main.$O: errstr.h init.h reboot.h +devdss.$O devmouse.$O mouse.$O screen.$O: screen.h +devusb.$O: ../port/usb.h +usbehci.$O usbohci.$O usbuhci.$O: ../port/usb.h usbehci.h uncached.h + +init.h:D: ../port/initcode.c init9.s + $CC ../port/initcode.c + $AS init9.s + $LD -l -R1 -s -o init.out init9.$O initcode.$O /$objtype/lib/libc.a + {echo 'uchar initcode[]={' + xd -1x init.h + +reboot.h:D: rebootcode.s cache.v7.s arm.s arm.h mem.h + $AS rebootcode.s + # -lc is only for memmove. -T arg is PADDR(REBOOTADDR) +# $LD -l -a -s -T0x100 -R4 -o reboot.out rebootcode.$O -lc >reboot.list + $LD -l -s -T0x100 -R4 -o reboot.out rebootcode.$O -lc + {echo 'uchar rebootcode[]={' + xd -1x reboot.out | + sed -e '1,2d' -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' + echo '};'} > reboot.h +errstr.h:D: ../port/mkerrstr ../port/error.h + rc ../port/mkerrstr > errstr.h diff --git a/sys/src/9/omap/mmu.c b/sys/src/9/omap/mmu.c new file mode 100755 index 000000000..05b4dc38c --- /dev/null +++ b/sys/src/9/omap/mmu.c @@ -0,0 +1,496 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include "arm.h" + +#define L1X(va) FEXT((va), 20, 12) +#define L2X(va) FEXT((va), 12, 8) + +enum { + L1lo = UZERO/MiB, /* L1X(UZERO)? */ + L1hi = (USTKTOP+MiB-1)/MiB, /* L1X(USTKTOP+MiB-1)? */ +}; + +#define ISHOLE(pte) ((pte) == 0) + +/* dump level 1 page table at virtual addr l1 */ +void +mmudump(PTE *l1) +{ + int i, type, rngtype; + uintptr pa, startva, startpa; + uvlong va, endva; + PTE pte; + +// pa -= MACHSIZE+1024; /* put level 2 entries below level 1 */ +// l2 = KADDR(pa); + + print("\n"); + endva = startva = startpa = 0; + rngtype = 0; + /* dump first level of ptes */ + for (va = i = 0; i < 4096; i++) { + pte = l1[i]; + pa = pte & ~(MB - 1); + type = pte & (Fine|Section|Coarse); + if (ISHOLE(pte)) { + if (endva != 0) { /* open range? close it */ + print("l1 maps va (%#lux-%#llux) -> pa %#lux type %#ux\n", + startva, endva-1, startpa, rngtype); + endva = 0; + } + } else { + if (endva == 0) { /* no open range? start one */ + startva = va; + startpa = pa; + rngtype = type; + } + endva = va + MB; /* continue the open range */ +// if (type == Coarse) { +// // could dump the l2 table for this l1 entry +// } + } + va += MB; + } + if (endva != 0) /* close an open range */ + print("l1 maps va (%#lux-%#llux) -> pa %#lux type %#ux\n", + startva, endva-1, startpa, rngtype); +} + +/* identity map the megabyte containing va, uncached */ +static void +idmap(PTE *l1, ulong va) +{ + va &= ~(MB-1); + l1[L1X(va)] = va | Dom0 | L1AP(Krw) | Section; +} + +/* map `mbs' megabytes from virt to phys */ +void +mmumap(uintptr virt, uintptr phys, int mbs) +{ + uint off; + PTE *l1; + + phys &= ~(MB-1); + virt &= ~(MB-1); + l1 = KADDR(ttbget()); + for (off = 0; mbs-- > 0; off += MB) + l1[L1X(virt + off)] = (phys + off) | Dom0 | L1AP(Krw) | Section; + cacheuwbinv(); + l2cacheuwbinv(); + mmuinvalidate(); +} + +/* identity map `mbs' megabytes from phys */ +void +mmuidmap(uintptr phys, int mbs) +{ + mmumap(phys, phys, mbs); +} + +void +mmuinit(void) +{ + uintptr pa; + PTE *l1, *l2; + + pa = ttbget(); + l1 = KADDR(pa); + + /* redundant with l.s; only covers first MB of 17MB */ + l1[L1X(VIRTIO)] = PHYSIO|Dom0|L1AP(Krw)|Section; + + idmap(l1, PHYSETHER); /* igep 9221 ethernet regs */ + idmap(l1, PHYSL4PROT); + idmap(l1, PHYSL3); + idmap(l1, PHYSSMS); + idmap(l1, PHYSDRC); + idmap(l1, PHYSGPMC); + + /* map high vectors to start of dram, but only 4K, not 1MB */ + pa -= MACHSIZE+2*1024; + l2 = KADDR(pa); + memset(l2, 0, 1024); + /* vectors step on u-boot, but so do page tables */ + l2[L2X(HVECTORS)] = PHYSDRAM|L2AP(Krw)|Small; + l1[L1X(HVECTORS)] = pa|Dom0|Coarse; /* vectors -> ttb-machsize-2k */ + coherence(); + + cacheuwbinv(); + l2cacheuwbinv(); + mmuinvalidate(); + + m->mmul1 = l1; +// mmudump(l1); /* DEBUG */ +} + +static void +mmul2empty(Proc* proc, int clear) +{ + PTE *l1; + Page **l2, *page; + + l1 = m->mmul1; + l2 = &proc->mmul2; + for(page = *l2; page != nil; page = page->next){ + if(clear) + memset(UINT2PTR(page->va), 0, BY2PG); + l1[page->daddr] = Fault; + l2 = &page->next; + } + *l2 = proc->mmul2cache; + proc->mmul2cache = proc->mmul2; + proc->mmul2 = nil; +} + +static void +mmul1empty(void) +{ +#ifdef notdef +/* there's a bug in here */ + PTE *l1; + + /* clean out any user mappings still in l1 */ + if(m->mmul1lo > L1lo){ + if(m->mmul1lo == 1) + m->mmul1[L1lo] = Fault; + else + memset(&m->mmul1[L1lo], 0, m->mmul1lo*sizeof(PTE)); + m->mmul1lo = L1lo; + } + if(m->mmul1hi < L1hi){ + l1 = &m->mmul1[m->mmul1hi]; + if((L1hi - m->mmul1hi) == 1) + *l1 = Fault; + else + memset(l1, 0, (L1hi - m->mmul1hi)*sizeof(PTE)); + m->mmul1hi = L1hi; + } +#else + memset(&m->mmul1[L1lo], 0, (L1hi - L1lo)*sizeof(PTE)); +#endif /* notdef */ +} + +void +mmuswitch(Proc* proc) +{ + int x; + PTE *l1; + Page *page; + + /* do kprocs get here and if so, do they need to? */ + if(m->mmupid == proc->pid && !proc->newtlb) + return; + m->mmupid = proc->pid; + + /* write back dirty and invalidate l1 caches */ + cacheuwbinv(); + + if(proc->newtlb){ + mmul2empty(proc, 1); + proc->newtlb = 0; + } + + mmul1empty(); + + /* move in new map */ + l1 = m->mmul1; + for(page = proc->mmul2; page != nil; page = page->next){ + x = page->daddr; + l1[x] = PPN(page->pa)|Dom0|Coarse; + /* know here that L1lo < x < L1hi */ + if(x+1 - m->mmul1lo < m->mmul1hi - x) + m->mmul1lo = x+1; + else + m->mmul1hi = x; + } + + /* make sure map is in memory */ + /* could be smarter about how much? */ + cachedwbse(&l1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE)); + + /* lose any possible stale tlb entries */ + mmuinvalidate(); + + //print("mmuswitch l1lo %d l1hi %d %d\n", + // m->mmul1lo, m->mmul1hi, proc->kp); +} + +void +flushmmu(void) +{ + int s; + + s = splhi(); + up->newtlb = 1; + mmuswitch(up); + splx(s); +} + +void +mmurelease(Proc* proc) +{ + Page *page, *next; + + /* write back dirty and invalidate l1 caches */ + cacheuwbinv(); + + mmul2empty(proc, 0); + for(page = proc->mmul2cache; page != nil; page = next){ + next = page->next; + if(--page->ref) + panic("mmurelease: page->ref %d", page->ref); + pagechainhead(page); + } + if(proc->mmul2cache && palloc.r.p) + wakeup(&palloc.r); + proc->mmul2cache = nil; + + mmul1empty(); + + /* make sure map is in memory */ + /* could be smarter about how much? */ + cachedwbse(&m->mmul1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE)); + + /* lose any possible stale tlb entries */ + mmuinvalidate(); +} + +void +putmmu(uintptr va, uintptr pa, Page* page) +{ + int x; + Page *pg; + PTE *l1, *pte; + + x = L1X(va); + l1 = &m->mmul1[x]; + //print("putmmu(%#p, %#p, %#p) ", va, pa, page->pa); + //print("mmul1 %#p l1 %#p *l1 %#ux x %d pid %d\n", + // m->mmul1, l1, *l1, x, up->pid); + if(*l1 == Fault){ + /* wasteful - l2 pages only have 256 entries - fix */ + if(up->mmul2cache == nil){ + /* auxpg since we don't need much? memset if so */ + pg = newpage(1, 0, 0); + pg->va = VA(kmap(pg)); + } + else{ + pg = up->mmul2cache; + up->mmul2cache = pg->next; + memset(UINT2PTR(pg->va), 0, BY2PG); + } + pg->daddr = x; + pg->next = up->mmul2; + up->mmul2 = pg; + + /* force l2 page to memory */ + cachedwbse((void *)pg->va, BY2PG); + + *l1 = PPN(pg->pa)|Dom0|Coarse; + cachedwbse(l1, sizeof *l1); + //print("l1 %#p *l1 %#ux x %d pid %d\n", l1, *l1, x, up->pid); + + if(x >= m->mmul1lo && x < m->mmul1hi){ + if(x+1 - m->mmul1lo < m->mmul1hi - x) + m->mmul1lo = x+1; + else + m->mmul1hi = x; + } + } + pte = UINT2PTR(KADDR(PPN(*l1))); + //print("pte %#p index %ld was %#ux\n", pte, L2X(va), *(pte+L2X(va))); + + /* protection bits are + * PTERONLY|PTEVALID; + * PTEWRITE|PTEVALID; + * PTEWRITE|PTEUNCACHED|PTEVALID; + */ + x = Small; + if(!(pa & PTEUNCACHED)) + x |= Cached|Buffered; + if(pa & PTEWRITE) + x |= L2AP(Urw); + else + x |= L2AP(Uro); + pte[L2X(va)] = PPN(pa)|x; + cachedwbse(&pte[L2X(va)], sizeof pte[0]); + + /* clear out the current entry */ + mmuinvalidateaddr(PPN(va)); + + /* write back dirty entries - we need this because the pio() in + * fault.c is writing via a different virt addr and won't clean + * its changes out of the dcache. Page coloring doesn't work + * on this mmu because the virtual cache is set associative + * rather than direct mapped. + */ + cachedwbinv(); + if(page->cachectl[0] == PG_TXTFLUSH){ + /* pio() sets PG_TXTFLUSH whenever a text pg has been written */ + cacheiinv(); + page->cachectl[0] = PG_NOFLUSH; + } + //print("putmmu %#p %#p %#p\n", va, pa, PPN(pa)|x); +} + +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 &= ~(Cached|Buffered); + mmuinvalidateaddr(va); + cachedwbinvse(pte, 4); + + return v; +} + +uintptr +mmukmap(uintptr va, uintptr pa, usize size) +{ + int x; + PTE *pte; + + /* + * Stub. + */ + assert(!(va & (1*MiB-1)) && !(pa & (1*MiB-1)) && size == 1*MiB); + + x = L1X(va); + pte = &m->mmul1[x]; + if(*pte != Fault) + return 0; + *pte = pa|Dom0|L1AP(Krw)|Section; + mmuinvalidateaddr(va); + cachedwbinvse(pte, 4); + + return va; +} + +uintptr +mmukunmap(uintptr va, uintptr pa, usize size) +{ + int x; + PTE *pte; + + /* + * Stub. + */ + assert(!(va & (1*MiB-1)) && !(pa & (1*MiB-1)) && size == 1*MiB); + + x = L1X(va); + pte = &m->mmul1[x]; + if(*pte != (pa|Dom0|L1AP(Krw)|Section)) + return 0; + *pte = Fault; + mmuinvalidateaddr(va); + cachedwbinvse(pte, 4); + + return va; +} + +/* + * Return the number of bytes that can be accessed via KADDR(pa). + * If pa is not a valid argument to KADDR, return 0. + */ +uintptr +cankaddr(uintptr pa) +{ + if(pa >= PHYSDRAM && pa < PHYSDRAM+memsize) + return PHYSDRAM+memsize - pa; + return 0; +} + +/* from 386 */ +void* +vmap(uintptr pa, usize size) +{ + uintptr pae, va; + usize o, osize; + + /* + * XXX - replace with new vm stuff. + * Crock after crock - the first 4MB is mapped with 2MB pages + * so catch that and return good values because the current mmukmap + * will fail. + */ + if(pa+size < 4*MiB) + return UINT2PTR(kseg0|pa); + + osize = size; + o = pa & (BY2PG-1); + pa -= o; + size += o; + size = ROUNDUP(size, BY2PG); + + va = kseg0|pa; + pae = mmukmap(va, pa, size); + if(pae == 0 || pae-size != pa) + panic("vmap(%#p, %ld) called from %#p: mmukmap fails %#p", + pa+o, osize, getcallerpc(&pa), pae); + + return UINT2PTR(va+o); +} + +/* from 386 */ +void +vunmap(void* v, usize size) +{ + /* + * XXX - replace with new vm stuff. + * Can't do this until do real vmap for all space that + * might be used, e.g. stuff below 1MB which is currently + * mapped automagically at boot but that isn't used (or + * at least shouldn't be used) by the kernel. + upafree(PADDR(v), size); + */ + USED(v, size); +} + +/* + * Notes. + * Everything is in domain 0; + * domain 0 access bits in the DAC register are set + * to Client, which means access is controlled by the + * permission values set in the PTE. + * + * L1 access control for the kernel is set to 1 (RW, + * no user mode access); + * L2 access control for the kernel is set to 1 (ditto) + * for all 4 AP sets; + * L1 user mode access is never set; + * L2 access control for user mode is set to either + * 2 (RO) or 3 (RW) depending on whether text or data, + * for all 4 AP sets. + * (To get kernel RO set AP to 0 and S bit in control + * register c1). + * Coarse L1 page-tables are used. They have 256 entries + * and so consume 1024 bytes per table. + * Small L2 page-tables are used. They have 1024 entries + * and so consume 4096 bytes per table. + * + * 4KiB. That's the size of 1) a page, 2) the + * size allocated for an L2 page-table page (note only 1KiB + * is needed per L2 page - to be dealt with later) and + * 3) the size of the area in L1 needed to hold the PTEs + * to map 1GiB of user space (0 -> 0x3fffffff, 1024 entries). + */ diff --git a/sys/src/9/omap/mouse.c b/sys/src/9/omap/mouse.c new file mode 100755 index 000000000..f0f62c8a1 --- /dev/null +++ b/sys/src/9/omap/mouse.c @@ -0,0 +1,255 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +/* + * mouse types + */ +enum +{ + Mouseother= 0, + Mouseserial= 1, + MousePS2= 2, +}; + +extern int mouseshifted; + +static QLock mousectlqlock; +static int mousetype; +static int intellimouse; +static int packetsize; +static int resolution; +static int accelerated; +static int mousehwaccel; +static char mouseport[5]; + +enum +{ + CMaccelerated, + CMhwaccel, + CMintellimouse, + CMlinear, + CMps2, + CMps2intellimouse, + CMres, + CMreset, + CMserial, +}; + +static Cmdtab mousectlmsg[] = +{ + CMaccelerated, "accelerated", 0, + CMhwaccel, "hwaccel", 2, + CMintellimouse, "intellimouse", 1, + CMlinear, "linear", 1, + CMps2, "ps2", 1, + CMps2intellimouse, "ps2intellimouse", 1, + CMres, "res", 0, + CMreset, "reset", 1, + CMserial, "serial", 0, +}; + +/* + * ps/2 mouse message is three bytes + * + * byte 0 - 0 0 SDY SDX 1 M R L + * byte 1 - DX + * byte 2 - DY + * + * shift & right button is the same as middle button + * + * Intellimouse and AccuPoint with extra buttons deliver + * byte 3 - 00 or 01 or FF according to extra button state. + * extra buttons are mapped in this code to buttons 4 and 5. + * AccuPoint generates repeated events for these buttons; +* it and Intellimouse generate 'down' events only, so + * user-level code is required to generate button 'up' events + * if they are needed by the application. + * Also on laptops with AccuPoint AND external mouse, the + * controller may deliver 3 or 4 bytes according to the type + * of the external mouse; code must adapt. + * + * On the NEC Versa series (and perhaps others?) we seem to + * lose a byte from the packet every once in a while, which + * means we lose where we are in the instruction stream. + * To resynchronize, if we get a byte more than two seconds + * after the previous byte, we assume it's the first in a packet. + */ +static void +ps2mouseputc(int c, int shift) +{ + static short msg[4]; + static int nb; + static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 3, 2, 3, 6, 7 }; + static ulong lasttick; + ulong m; + int buttons, dx, dy; + + shift |= mouseshifted; + m = MACHP(0)->ticks; + if(TK2SEC(m - lasttick) > 2) + nb = 0; + lasttick = m; + if(nb==0 && (c&0xc8)!=0x08) + if(intellimouse && (c==0x00 || c==0x01 || c==0xFF)){ + packetsize = 4; + return; + } + + msg[nb] = c; + if(++nb == packetsize){ + nb = 0; + if(msg[0] & 0x10) + msg[1] |= 0xFF00; + if(msg[0] & 0x20) + msg[2] |= 0xFF00; + + buttons = b[(msg[0]&7) | (shift ? 8 : 0)]; + if(intellimouse && packetsize==4){ + if((msg[3]&0xc8) == 0x08){ + packetsize = 3; + msg[0] = msg[3]; + nb = 1; + }else{ + if((msg[3] >> 3) & 1) + buttons |= 1<<3; + else if(msg[3] & 0x7) + buttons |= 1<<4; + } + } + dx = msg[1]; + dy = -msg[2]; + mousetrack(dx, dy, buttons, TK2MS(MACHP(0)->ticks)); + } +} + +/* + * set up a ps2 mouse + */ +static void +ps2mouse(void) +{ + if(mousetype == MousePS2) + return; + +// i8042auxenable(ps2mouseputc); +// i8042auxcmd(0xEA); // TODO +// i8042auxcmd(0xF4); + + mousetype = MousePS2; + packetsize = 3; + mousehwaccel = 1; +} + +/* + * The PS/2 Trackpoint multiplexor on the IBM Thinkpad T23 ignores + * acceleration commands. It is supposed to pass them on + * to the attached device, but my Logitech mouse is simply + * not behaving any differently. For such devices, we allow + * the user to use "hwaccel off" to tell us to back off to + * software acceleration even if we're using the PS/2 port. + * (Serial mice are always software accelerated.) + * For more information on the Thinkpad multiplexor, see + * http://wwwcssrv.almaden.ibm.com/trackpoint/ + */ +static void +setaccelerated(int x) +{ + accelerated = x; + mouseaccelerate(x); +} + +static void +setlinear(void) +{ + accelerated = 0; + mouseaccelerate(0); +} + +static void +setres(int n) +{ + resolution = n; +} + +static void +setintellimouse(void) +{ + intellimouse = 1; + packetsize = 4; +} + +static void +resetmouse(void) +{ + packetsize = 3; +} + +void +mousectl(Cmdbuf *cb) +{ + Cmdtab *ct; + + qlock(&mousectlqlock); + if(waserror()){ + qunlock(&mousectlqlock); + nexterror(); + } + + ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg)); + switch(ct->index){ + case CMaccelerated: + setaccelerated(cb->nf == 1? 1: atoi(cb->f[1])); + break; + case CMintellimouse: + setintellimouse(); + break; + case CMlinear: + setlinear(); + break; + case CMps2: + intellimouse = 0; + break; + case CMps2intellimouse: + setintellimouse(); + break; + case CMres: + if(cb->nf >= 2) + setres(atoi(cb->f[1])); + else + setres(1); + break; + case CMreset: + resetmouse(); + if(accelerated) + setaccelerated(accelerated); + if(resolution) + setres(resolution); + if(intellimouse) + setintellimouse(); + break; + case CMserial: + error("serial mice not supported"); + break; + case CMhwaccel: + if(strcmp(cb->f[1], "on")==0) + mousehwaccel = 1; + else if(strcmp(cb->f[1], "off")==0) + mousehwaccel = 0; + else + cmderror(cb, "bad mouse control message"); + } + + qunlock(&mousectlqlock); + poperror(); +} diff --git a/sys/src/9/omap/notes/movm.w b/sys/src/9/omap/notes/movm.w new file mode 100755 index 000000000..a2b22f1e1 --- /dev/null +++ b/sys/src/9/omap/notes/movm.w @@ -0,0 +1,22 @@ +gorka writes: +--- +I have userspace on the gumstix [xscale, not omap]. The problem that +got me in trouble was that in lexception.s (or l.s), + + MOVM.DB.W [R0-R14], (R13) + +works differently for this architecture (and probably for others, as +it is unclear how it should behave by reading the arm specs). This +happens only for kernel faults as the others (syscall, user faults) +use MOVM.DB.W.S which uses the banked user registers. + +The problem is that in this arch the value of R13 saved is the value +after R13 itself has been modified, whereas in the others (bitsy, +pico...), it was the value before. Adding 4*15 to the stack before +the RFE solves the problem. +--- + +In fact, the 2005 ARM arch. ref. man. (ARM DDI 0100I) says, under STM (1), +that if Rn appears in the set of registers (and isn't the first one) +and .W is specified, the stored value of Rn is unpredictable. +The arm v7-ar arch. ref. man. says such usage is obsolete. diff --git a/sys/src/9/omap/nvram b/sys/src/9/omap/nvram new file mode 100755 index 000000000..a64a5a93f Binary files /dev/null and b/sys/src/9/omap/nvram differ diff --git a/sys/src/9/omap/random.c b/sys/src/9/omap/random.c new file mode 100755 index 000000000..1f7c0983d --- /dev/null +++ b/sys/src/9/omap/random.c @@ -0,0 +1,138 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + + +struct Rb +{ + QLock; + Rendez producer; + Rendez consumer; + ulong randomcount; + uchar buf[128]; + uchar *ep; + uchar *rp; + uchar *wp; + uchar next; + uchar wakeme; + ushort bits; + ulong randn; +} rb; + +static int +rbnotfull(void*) +{ + int i; + + i = rb.rp - rb.wp; + return i != 1 && i != (1 - sizeof(rb.buf)); +} + +static int +rbnotempty(void*) +{ + return rb.wp != rb.rp; +} + +static void +genrandom(void*) +{ + up->basepri = PriNormal; + up->priority = up->basepri; + + for(;;){ + for(;;) + if(++rb.randomcount > 100000) + break; + if(anyhigher()) + sched(); + if(!rbnotfull(0)) + sleep(&rb.producer, rbnotfull, 0); + } +} + +/* + * produce random bits in a circular buffer + */ +static void +randomclock(void) +{ + if(rb.randomcount == 0 || !rbnotfull(0)) + return; + + rb.bits = (rb.bits<<2) ^ rb.randomcount; + rb.randomcount = 0; + + rb.next++; + if(rb.next != 8/2) + return; + rb.next = 0; + + *rb.wp ^= rb.bits; + if(rb.wp+1 == rb.ep) + rb.wp = rb.buf; + else + rb.wp = rb.wp+1; + + if(rb.wakeme) + wakeup(&rb.consumer); +} + +void +randominit(void) +{ + addclock0link(randomclock, 1000/HZ); + rb.ep = rb.buf + sizeof(rb.buf); + rb.rp = rb.wp = rb.buf; + kproc("genrandom", genrandom, 0); +} + +/* + * consume random bytes from a circular buffer + */ +ulong +randomread(void *xp, ulong n) +{ + uchar *e, *p; + ulong x; + + p = xp; + + if(waserror()){ + qunlock(&rb); + nexterror(); + } + + qlock(&rb); + for(e = p + n; p < e; ){ + if(rb.wp == rb.rp){ + rb.wakeme = 1; + wakeup(&rb.producer); + sleep(&rb.consumer, rbnotempty, 0); + rb.wakeme = 0; + continue; + } + + /* + * beating clocks will be predictable if + * they are synchronized. Use a cheap pseudo + * random number generator to obscure any cycles. + */ + x = rb.randn*1103515245 ^ *rb.rp; + *p++ = rb.randn = x; + + if(rb.rp+1 == rb.ep) + rb.rp = rb.buf; + else + rb.rp = rb.rp+1; + } + qunlock(&rb); + poperror(); + + wakeup(&rb.producer); + + return n; +} diff --git a/sys/src/9/omap/rebootcode.s b/sys/src/9/omap/rebootcode.s new file mode 100755 index 000000000..f07146f81 --- /dev/null +++ b/sys/src/9/omap/rebootcode.s @@ -0,0 +1,209 @@ +/* + * omap3530 reboot code + * + * must fit in 11K to avoid stepping on PTEs; see mem.h. + * + * R11 is used by the loader as a temporary, so avoid it. + */ +#include "arm.s" + +/* + * Turn off MMU, then copy the new kernel to its correct location + * in physical memory. Then jump to the start of the kernel. + */ + +/* main(PADDR(entry), PADDR(code), size); */ +TEXT main(SB), 1, $-4 + MOVW $setR12(SB), R12 + + MOVW R0, p1+0(FP) /* destination, passed in R0 */ + + MOVW CPSR, R0 + ORR $(PsrDirq|PsrDfiq), R0 + MOVW R0, CPSR /* splhi */ + BARRIERS + +WAVE('R') + MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + BIC $CpACasa, R1 /* no speculative I access forwarding to mem */ + /* slow down */ + ORR $(CpACcachenopipe|CpACcp15serial|CpACcp15waitidle|CpACcp15pipeflush), R1 + MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl + BARRIERS + + BL cachesoff(SB) + /* now back in 29- or 26-bit addressing, mainly for SB */ + /* double mapping of PHYSDRAM & KZERO now in effect */ + + /* + * turn the MMU off + */ + +WAVE('e') + /* first switch to PHYSDRAM-based addresses */ + DMB + + MOVW $KSEGM, R7 /* clear segment bits */ + MOVW $PHYSDRAM, R0 /* set dram base bits */ + BIC R7, R12 /* adjust SB */ + ORR R0, R12 + + BL _r15warp(SB) + /* don't care about saving R14; we're not returning */ + + /* + * now running in PHYSDRAM segment, not KZERO. + */ + +WAVE('b') + SUB $12, SP /* paranoia */ + BL cacheuwbinv(SB) + ADD $12, SP /* paranoia */ + + /* invalidate mmu mappings */ + MOVW $KZERO, R0 /* some valid virtual address */ + MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv + BARRIERS + +WAVE('o') + MRC CpSC, 0, R0, C(CpCONTROL), C(0) + BIC $(CpCmmu|CpCdcache|CpCicache), R0 + MCR CpSC, 0, R0, C(CpCONTROL), C(0) /* mmu off */ + BARRIERS + +WAVE('o') + /* copy in arguments from stack frame before moving stack */ + MOVW p2+4(FP), R4 /* phys source */ + MOVW n+8(FP), R5 /* byte count */ + MOVW p1+0(FP), R6 /* phys destination */ + + /* set up a new stack for local vars and memmove args */ + MOVW R6, SP /* tiny trampoline stack */ + SUB $(0x20 + 4), SP /* back up before a.out header */ + +// MOVW R14, -48(SP) /* store return addr */ + SUB $48, SP /* allocate stack frame */ + + MOVW R5, 40(SP) /* save count */ + MOVW R6, 44(SP) /* save dest/entry */ + + DELAY(printloop2, 2) +WAVE('t') + + MOVW 40(SP), R5 /* restore count */ + MOVW 44(SP), R6 /* restore dest/entry */ + MOVW R6, 0(SP) /* normally saved LR goes here */ + MOVW R6, 4(SP) /* push dest */ + MOVW R6, R0 + MOVW R4, 8(SP) /* push src */ + MOVW R5, 12(SP) /* push size */ + BL memmove(SB) + +WAVE('-') + /* + * flush caches + */ + BL cacheuwbinv(SB) + +WAVE('>') + DELAY(printloopret, 1) +WAVE('\r') + DELAY(printloopnl, 1) +WAVE('\n') +/* + * jump to kernel entry point. Note the true kernel entry point is + * the virtual address KZERO|R6, but this must wait until + * the MMU is enabled by the kernel in l.s + */ + MOVW 44(SP), R6 /* restore R6 (dest/entry) */ + ORR R6, R6 /* NOP: avoid link bug */ + B (R6) +WAVE('?') + B 0(PC) + +/* + * turn the caches off, double map PHYSDRAM & KZERO, invalidate TLBs, revert + * to tiny addresses. upon return, it will be safe to turn off the mmu. + */ +TEXT cachesoff(SB), 1, $-4 + MOVM.DB.W [R14,R1-R10], (R13) /* save regs on stack */ + MOVW CPSR, R0 + ORR $(PsrDirq|PsrDfiq), R0 + MOVW R0, CPSR + BARRIERS + + SUB $12, SP /* paranoia */ + BL cacheuwbinv(SB) + ADD $12, SP /* paranoia */ + + MRC CpSC, 0, R0, C(CpCONTROL), C(0) + BIC $(CpCicache|CpCdcache), R0 + MCR CpSC, 0, R0, C(CpCONTROL), C(0) /* caches off */ + BARRIERS + + /* + * caches are off + */ + + /* invalidate stale TLBs before changing them */ + MOVW $KZERO, R0 /* some valid virtual address */ + MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv + BARRIERS + + /* redo double map of PHYSDRAM, KZERO */ + MOVW $PHYSDRAM, R3 + CMP $KZERO, R3 + BEQ noun2map + MOVW $(L1+L1X(PHYSDRAM)), R4 /* address of PHYSDRAM's PTE */ + MOVW $PTEDRAM, R2 /* PTE bits */ + MOVW $DOUBLEMAPMBS, R5 +_ptrdbl: + ORR R3, R2, R1 /* first identity-map 0 to 0, etc. */ + MOVW R1, (R4) + ADD $4, R4 /* bump PTE address */ + ADD $MiB, R3 /* bump pa */ + SUB.S $1, R5 + BNE _ptrdbl +noun2map: + + /* + * flush stale TLB entries + */ + + BARRIERS + MOVW $KZERO, R0 /* some valid virtual address */ + MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv + BARRIERS + + /* switch back to PHYSDRAM addressing, mainly for SB */ + MOVW $KSEGM, R7 /* clear segment bits */ + MOVW $PHYSDRAM, R0 /* set dram base bits */ + BIC R7, R12 /* adjust SB */ + ORR R0, R12 + BIC R7, SP + ORR R0, SP + + MOVM.IA.W (R13), [R14,R1-R10] /* restore regs from stack */ + + MOVW $KSEGM, R0 /* clear segment bits */ + BIC R0, R14 /* adjust link */ + MOVW $PHYSDRAM, R0 /* set dram base bits */ + ORR R0, R14 + + RET + +TEXT _r15warp(SB), 1, $-4 + BIC R7, R14 /* link */ + ORR R0, R14 + + BIC R7, R13 /* SP */ + ORR R0, R13 + RET + +TEXT panic(SB), 1, $-4 /* stub */ +WAVE('?') + RET +TEXT pczeroseg(SB), 1, $-4 /* stub */ + RET + +#include "cache.v7.s" diff --git a/sys/src/9/omap/screen.c b/sys/src/9/omap/screen.c new file mode 100755 index 000000000..e47fd2821 --- /dev/null +++ b/sys/src/9/omap/screen.c @@ -0,0 +1,791 @@ +/* + * ti omap35 display subsystem (dss) + * + * can handle 2ⁿ bits per pixel for 0 < n ≤ 4, and 12 and 24 bits. + * can handle 1024×768 at 60 Hz with pixel clock of 63.5 MHz + * 1280×800 at 59.91 Hz with pixel clock of 71 MHz + * 1400×1050 lcd at 50 MHz with pixel clock of 75 MHz + * has 256 24-bit entries in RGB palette + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" +// #include "gamma.h" + +enum { + Tabstop = 4, /* should be 8 */ + Scroll = 8, /* lines to scroll at one time */ + /* + * screen settings for Wid and Ht, should a bit more dynamic? + * http://www.epanorama.net/faq/vga2rgb/calc.html + * used to calculate settings. + */ + +// Hbp = (248-1) << 20, +// Hfp = (48-1) << 8, +// Hsw = 112-1, + +// Vbp = 38 << 20, +// Vfp = 1 << 8, +// Vsw = 3, + + Tft = 0x60, + + Loadmode = 2 << 1, + Fifosize = 0x400, + + /* dispc sysconfig */ + Midlemode = 2 << 12, + Sidlemode = 2 << 3, + EnableWakeup = 1 << 2, + Autoidle = 1 << 0, + + /* dispc pool_freq */ + Ipc = 1 << 14, + Ihs = 1 << 13, + Ivs = 1 << 12, + Acb = 0x28, + + /* gfx attribs */ + Burstsize = 2 << 6, + Format = 6 << 1, + Gfxenable = 1 << 0, + + /* dispc control */ + Gpout1 = 1 << 16, + Gpout0 = 1 << 15, + Tftdata = 3 << 8, + Digital = 1 << 6, + Lcd = 1 << 5, + Stntft = 1 << 3, + Digitalen = 1 << 1, +// Lcden = 1 << 0, /* unused */ +}; + +typedef struct Dispcregs Dispc; +typedef struct Dssregs Dss; +typedef struct Ioregs Ioregs; + +struct Ioregs { /* common registers, 68 (0x44) bytes */ + ulong rev; + uchar _pad0[0x10-0x4]; + ulong sysconf; + ulong sysstat; + ulong irqstat1; + + /* Dispc only regs */ + ulong irqen1; + ulong wkupen; + ulong _pad1; + ulong irqsts2; + ulong irqen2; + ulong _pad2[4]; + + ulong ctrl; +}; + +struct Dssregs { /* display subsys at 0x48050000 */ + Ioregs; + ulong sdicrtl; + ulong pllcrtl; + uchar _pad3[0x5c-0x4c]; + ulong sdistat; +}; + +struct Dispcregs { /* display ctlr at 0x48050400 */ + Ioregs; + ulong config; + ulong _pad3; + ulong defaultcolor[2]; + ulong transcolor[2]; + ulong linestat; + ulong linenum; + ulong timing_h; + ulong timing_v; + ulong pol_req; + ulong divisor; + ulong alpha; + ulong digsize; + ulong lcdsize; + + ulong base[2]; /* should allocate both to avoid dithering */ + ulong pos; + ulong size; + ulong _pad4[4]; + ulong attrib; + ulong fifothr; + ulong fifosize; + ulong rowinc; + ulong pixelinc; + ulong winskip; + ulong palette; /* gfx_table_ba */ + uchar _pad5[0x5d4 - 0x4bc]; + + ulong datacycle[3]; + uchar _pad5[0x620 - 0x5e0]; + + ulong cprcoefr; + ulong cprcoefg; + ulong cprcoefb; + ulong preload; +}; + +int drawdebug; +Point ZP = {0, 0}; +Cursor arrow = { + { -1, -1 }, + { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, + 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, + 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, + }, + { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, + 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, + 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, + 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, + }, +}; + +OScreen oscreen; +Settings settings[] = { +[Res800x600] { 800, 600, 60, RGB16, 40000, 88, 40, 128, 23, 1, 5, }, +[Res1024x768] { 1024, 768, 60, RGB16, 65000, 160, 24, 136, 29, 3, 7, }, +[Res1280x1024] { 1280, 1024, 60, RGB16, 108000, 248, 48, 112, 38, 1, 4, }, +[Res1400x1050] { 1400, 1050, 50, RGB16, 108000, 248, 48, 112, 38, 1, 4, }, // TODO +}; +Omap3fb *framebuf; +Memimage *gscreen; + +static Memdata xgdata; + +static Memimage xgscreen = +{ + { 0, 0, Wid, Ht }, /* r */ + { 0, 0, Wid, Ht }, /* clipr */ + Depth, /* depth */ + 3, /* nchan */ + RGB16, /* chan */ + nil, /* cmap */ + &xgdata, /* data */ + 0, /* zero */ + Wid*(Depth/BI2BY)/BY2WD, /* width in words of a single scan line */ + 0, /* layer */ + 0, /* flags */ +}; + +static Memimage *conscol; +static Memimage *back; + +static Memsubfont *memdefont; + +static Lock screenlock; + +static Point curpos; +static int h, w; +static int landscape = 0; /* screen orientation, default is 0: portrait */ +static ushort *vscreen; /* virtual screen */ +static Rectangle window; + +static Dispc *dispc = (Dispc *)PHYSDISPC; +static Dss *dss = (Dss *)PHYSDSS; + +static void omapscreenputs(char *s, int n); +static ulong rep(ulong, int); +static void screenputc(char *buf); +static void screenwin(void); + +/* + * Software cursor. + */ +int swvisible; /* is the cursor visible? */ +int swenabled; /* is the cursor supposed to be on the screen? */ +Memimage* swback; /* screen under cursor */ +Memimage* swimg; /* cursor image */ +Memimage* swmask; /* cursor mask */ +Memimage* swimg1; +Memimage* swmask1; + +Point swoffset; +Rectangle swrect; /* screen rectangle in swback */ +Point swpt; /* desired cursor location */ +Point swvispt; /* actual cursor location */ +int swvers; /* incremented each time cursor image changes */ +int swvisvers; /* the version on the screen */ + +static void +lcdoff(void) +{ + dispc->ctrl &= ~1; /* disable the lcd */ + coherence(); + + dispc->irqstat1 |= 1; /* set framedone */ + coherence(); + + /* the lcd never comes ready, so don't bother with this */ +#ifdef notdef + /* spin until the frame is complete, but not forever */ + for(cnt = 50; !(dispc->irqstat1 & 1) && cnt-- > 0; ) + delay(10); +#endif + delay(20); /* worst case for 1 frame, 50Hz */ +} + +static void +dssstart(void) +{ + /* should reset the dss system */ + dss->sysconf |= 1; + coherence(); +} + +/* see spruf98i §15.6.7.4.2 */ +static void +configdispc(void) +{ + Settings *sp; + + sp = oscreen.settings; + dss->ctrl &= 0x78; /* choose dss clock */ + dispc->sysconf = Midlemode | Sidlemode | EnableWakeup | Autoidle; + dispc->config = Loadmode; + coherence(); + + /* pll */ + dispc->defaultcolor[0] = 0; /* set background color to black? */ + dispc->defaultcolor[1] = 0; + dispc->transcolor[0] = 0; /* set transparency to full */ + dispc->transcolor[1] = 0; + + dispc->timing_h = (sp->hbp-1) << 20 | (sp->hfp-1) << 8 | + (sp->hsw-1); + dispc->timing_v = sp->vbp << 20 | sp->vfp << 8 | + (sp->vsw-1); + + dispc->pol_req = Ipc | Ihs | Ivs | Acb; + dispc->divisor = 1 << 16 | HOWMANY(432000, sp->pixelclock); + + dispc->lcdsize = (sp->ht - 1) << 16 | (sp->wid - 1); + coherence(); + + dispc->base[0] = PADDR(framebuf->pixel); + dispc->base[1] = PADDR(framebuf->pixel); + + dispc->pos = 0; /* place screen in the left corner */ + /* use the whole screen */ + dispc->size = (sp->ht - 1) << 16 | (sp->wid - 1); + + /* what mode does plan 9 use for fb? */ + dispc->attrib = Burstsize | Format | Gfxenable; + + dispc->preload = Tft; + dispc->fifosize = Fifosize; + /* 1008 is max for our Burstsize */ + dispc->fifothr = (Fifosize - 1) << 16 | (1008 - 1); + + /* 1 byte is one pixel (not true, we use 2 bytes per pixel) */ + dispc->rowinc = 1; + dispc->pixelinc = 1; + dispc->winskip = 0; /* don't skip anything */ + coherence(); + + // dispc->palette = PADDR(framebuf->palette); +} + +static void +lcdon(int enable) +{ + dispc->ctrl = Gpout1 | Gpout0 | Tftdata | Digital | Lcd | Stntft | + Digitalen | enable; + coherence(); + delay(10); +} + +static void +lcdstop(void) +{ + configscreengpio(); + screenclockson(); + + lcdoff(); +} + +static void +lcdinit(void) +{ + lcdstop(); + + dssstart(); + configdispc(); +} + +/* Paint the image data with blue pixels */ +void +screentest(void) +{ + int i; + + for (i = nelem(framebuf->pixel) - 1; i >= 0; i--) + framebuf->pixel[i] = 0x1f; /* blue */ +// memset(framebuf->pixel, ~0, sizeof framebuf->pixel); /* white */ +} + +void +screenpower(int on) +{ + blankscreen(on == 0); +} + +/* + * called with drawlock locked for us, most of the time. + * kernel prints at inopportune times might mean we don't + * hold the lock, but memimagedraw is now reentrant so + * that should be okay: worst case we get cursor droppings. + */ +void +swcursorhide(void) +{ + if(swvisible == 0) + return; + if(swback == nil) + return; + swvisible = 0; + memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S); + flushmemscreen(swrect); +} + +void +swcursoravoid(Rectangle r) +{ + if(swvisible && rectXrect(r, swrect)) + swcursorhide(); +} + +void +swcursordraw(void) +{ + if(swvisible) + return; + if(swenabled == 0) + return; + if(swback == nil || swimg1 == nil || swmask1 == nil) + return; +// assert(!canqlock(&drawlock)); // assertion fails on omap + swvispt = swpt; + swvisvers = swvers; + swrect = rectaddpt(Rect(0,0,16,16), swvispt); + memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S); + memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD); + flushmemscreen(swrect); + swvisible = 1; +} + +int +cursoron(int dolock) +{ + if (dolock) + lock(&oscreen); + cursoroff(0); + swcursordraw(); + if (dolock) + unlock(&oscreen); + return 0; +} + +void +cursoroff(int dolock) +{ + if (dolock) + lock(&oscreen); + swcursorhide(); + if (dolock) + unlock(&oscreen); +} + +void +swload(Cursor *curs) +{ + uchar *ip, *mp; + int i, j, set, clr; + + if(!swimg || !swmask || !swimg1 || !swmask1) + return; + /* + * Build cursor image and mask. + * Image is just the usual cursor image + * but mask is a transparent alpha mask. + * + * The 16x16x8 memimages do not have + * padding at the end of their scan lines. + */ + ip = byteaddr(swimg, ZP); + mp = byteaddr(swmask, ZP); + for(i=0; i<32; i++){ + set = curs->set[i]; + clr = curs->clr[i]; + for(j=0x80; j; j>>=1){ + *ip++ = set&j ? 0x00 : 0xFF; + *mp++ = (clr|set)&j ? 0xFF : 0x00; + } + } + swoffset = curs->offset; + swvers++; + memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S); + memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S); +} + +/* called from devmouse */ +void +setcursor(Cursor* curs) +{ + cursoroff(1); + oscreen.Cursor = *curs; + swload(curs); + cursoron(1); +} + +int +swmove(Point p) +{ + swpt = addpt(p, swoffset); + return 0; +} + +void +swcursorclock(void) +{ + int x; + + if(!swenabled) + return; + swmove(mousexy()); + if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers) + return; + + x = splhi(); + if(swenabled) + if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers) + if(canqlock(&drawlock)){ + swcursorhide(); + swcursordraw(); + qunlock(&drawlock); + } + splx(x); +} + +void +swcursorinit(void) +{ + static int init; + + if(!init){ + init = 1; + addclock0link(swcursorclock, 10); + } + if(swback){ + freememimage(swback); + freememimage(swmask); + freememimage(swmask1); + freememimage(swimg); + freememimage(swimg1); + } + + swback = allocmemimage(Rect(0,0,32,32), gscreen->chan); + swmask = allocmemimage(Rect(0,0,16,16), GREY8); + swmask1 = allocmemimage(Rect(0,0,16,16), GREY1); + swimg = allocmemimage(Rect(0,0,16,16), GREY8); + swimg1 = allocmemimage(Rect(0,0,16,16), GREY1); + if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){ + print("software cursor: allocmemimage fails\n"); + return; + } + + memfillcolor(swmask, DOpaque); + memfillcolor(swmask1, DOpaque); + memfillcolor(swimg, DBlack); + memfillcolor(swimg1, DBlack); +} + +/* called from main and possibly later from devdss to change resolution */ +void +screeninit(void) +{ + static int first = 1; + + if (first) { + iprint("screeninit..."); + oscreen.settings = &settings[Res1280x1024]; + + lcdstop(); + if (framebuf) + free(framebuf); + /* mode is 16*32 = 512 */ + framebuf = xspanalloc(sizeof *framebuf, 16*32, 0); + } + + lcdinit(); + lcdon(1); + if (first) { + memimageinit(); + memdefont = getmemdefont(); + screentest(); + } + + xgdata.ref = 1; + xgdata.bdata = (uchar *)framebuf->pixel; + + gscreen = &xgscreen; + gscreen->r = Rect(0, 0, Wid, Ht); + gscreen->clipr = gscreen->r; + /* width, in words, of a single scan line */ + gscreen->width = Wid * (Depth / BI2BY) / BY2WD; + flushmemscreen(gscreen->r); + + blanktime = 3; /* minutes */ + + if (first) { + iprint("on: blue for 3 seconds..."); + delay(3*1000); + iprint("\n"); + + screenwin(); /* draw border & top orange bar */ + screenputs = omapscreenputs; + iprint("screen: frame buffer at %#p for %dx%d\n", + framebuf, oscreen.settings->wid, oscreen.settings->ht); + + swenabled = 1; + swcursorinit(); /* needs gscreen set */ + setcursor(&arrow); + + first = 0; + } +} + +/* flushmemscreen should change buffer? */ +void +flushmemscreen(Rectangle r) +{ + ulong start, end; + + if (r.min.x < 0) + r.min.x = 0; + if (r.max.x > Wid) + r.max.x = Wid; + if (r.min.y < 0) + r.min.y = 0; + if (r.max.y > Ht) + r.max.y = Ht; + if (rectclip(&r, gscreen->r) == 0) + return; + start = (ulong)&framebuf->pixel[r.min.y*Wid + r.min.x]; + end = (ulong)&framebuf->pixel[(r.max.y - 1)*Wid + r.max.x -1]; + cachedwbse((ulong *)start, end - start); +} + +/* + * export screen to devdraw + */ +uchar* +attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen) +{ + *r = gscreen->r; + *d = gscreen->depth; + *chan = gscreen->chan; + *width = gscreen->width; + *softscreen = (landscape == 0); + return (uchar *)gscreen->data->bdata; +} + +void +getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb) +{ + USED(p, pr, pg, pb); +} + +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + USED(p, r, g, b); + return 0; +} + +void +blankscreen(int blank) +{ + if (blank) + lcdon(0); + else { + lcdinit(); + lcdon(1); + } +} + +static void +omapscreenputs(char *s, int n) +{ + int i; + Rune r; + char buf[4]; + + if (!islo()) { + /* don't deadlock trying to print in interrupt */ + if (!canlock(&screenlock)) + return; /* discard s */ + } else + lock(&screenlock); + + while (n > 0) { + i = chartorune(&r, s); + if (i == 0) { + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + screenputc(buf); + } + unlock(&screenlock); +} + +static void +screenwin(void) +{ + char *greet; + Memimage *orange; + Point p, q; + Rectangle r; + + memsetchan(gscreen, RGB16); + + back = memwhite; + conscol = memblack; + + orange = allocmemimage(Rect(0, 0, 1, 1), RGB16); + orange->flags |= Frepl; + orange->clipr = gscreen->r; + orange->data->bdata[0] = 0x40; /* magic: colour? */ + orange->data->bdata[1] = 0xfd; /* magic: colour? */ + + w = memdefont->info[' '].width; + h = memdefont->height; + + r = insetrect(gscreen->r, 4); + + memimagedraw(gscreen, r, memblack, ZP, memopaque, ZP, S); + window = insetrect(r, 4); + memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP, S); + + memimagedraw(gscreen, Rect(window.min.x, window.min.y, + window.max.x, window.min.y + h + 5 + 6), orange, ZP, nil, ZP, S); + freememimage(orange); + window = insetrect(window, 5); + + greet = " Plan 9 Console "; + p = addpt(window.min, Pt(10, 0)); + q = memsubfontwidth(memdefont, greet); + memimagestring(gscreen, p, conscol, ZP, memdefont, greet); + flushmemscreen(r); + window.min.y += h + 6; + curpos = window.min; + window.max.y = window.min.y + ((window.max.y - window.min.y) / h) * h; +} + +static void +scroll(void) +{ + int o; + Point p; + Rectangle r; + + /* move window contents up Scroll text lines */ + o = Scroll * h; + r = Rpt(window.min, Pt(window.max.x, window.max.y - o)); + p = Pt(window.min.x, window.min.y + o); + memimagedraw(gscreen, r, gscreen, p, nil, p, S); + flushmemscreen(r); + + /* clear the bottom Scroll text lines */ + r = Rpt(Pt(window.min.x, window.max.y - o), window.max); + memimagedraw(gscreen, r, back, ZP, nil, ZP, S); + flushmemscreen(r); + + curpos.y -= o; +} + +static void +screenputc(char *buf) +{ + int w; + uint pos; + Point p; + Rectangle r; + static int *xp; + static int xbuf[256]; + + if (xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) + xp = xbuf; + + switch (buf[0]) { + case '\n': + if (curpos.y + h >= window.max.y) + scroll(); + curpos.y += h; + screenputc("\r"); + break; + case '\r': + xp = xbuf; + curpos.x = window.min.x; + break; + case '\t': + p = memsubfontwidth(memdefont, " "); + w = p.x; + if (curpos.x >= window.max.x - Tabstop * w) + screenputc("\n"); + + pos = (curpos.x - window.min.x) / w; + pos = Tabstop - pos % Tabstop; + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x + pos * w, curpos.y + h); + memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); + flushmemscreen(r); + curpos.x += pos * w; + break; + case '\b': + if (xp <= xbuf) + break; + xp--; + r = Rect(*xp, curpos.y, curpos.x, curpos.y + h); + memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); + flushmemscreen(r); + curpos.x = *xp; + break; + case '\0': + break; + default: + p = memsubfontwidth(memdefont, buf); + w = p.x; + + if (curpos.x >= window.max.x - w) + screenputc("\n"); + + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x + w, curpos.y + h); + memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); + memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf); + flushmemscreen(r); + curpos.x += w; + } +} diff --git a/sys/src/9/omap/screen.h b/sys/src/9/omap/screen.h new file mode 100755 index 000000000..ba9b36bd3 --- /dev/null +++ b/sys/src/9/omap/screen.h @@ -0,0 +1,105 @@ +typedef struct Cursor Cursor; +typedef struct Cursorinfo Cursorinfo; +typedef struct OScreen OScreen; +typedef struct Omap3fb Omap3fb; +typedef struct Settings Settings; + +struct Cursorinfo +{ + Cursor; + Lock; +}; + +extern Cursor arrow; +extern Cursorinfo cursor; + +/* devmouse.c */ +extern void mousetrack(int, int, int, int); +extern Point mousexy(void); + +extern void mouseaccelerate(int); +extern void mouseresize(void); + +/* screen.c */ +extern uchar* attachscreen(Rectangle*, ulong*, int*, int*, int*); +extern void flushmemscreen(Rectangle); +extern int cursoron(int); +extern void cursoroff(int); +extern void setcursor(Cursor*); +extern int screensize(int, int, int, ulong); +extern int screenaperture(int, int); +extern Rectangle physgscreenr; /* actual monitor size */ +extern void blankscreen(int); + +extern void swcursorinit(void); +extern void swcursorhide(void); +extern void swcursoravoid(Rectangle); +extern void swcursorunhide(void); + +/* devdraw.c */ +extern void deletescreenimage(void); +extern void resetscreenimage(void); +extern int drawhasclients(void); +extern ulong blanktime; +extern void setscreenimageclipr(Rectangle); +extern void drawflush(void); +extern int drawidletime(void); +extern QLock drawlock; + +#define ishwimage(i) 0 /* for ../port/devdraw.c */ + +/* for communication between devdss.c and screen.c */ + +enum { + /* maxima */ + Wid = 1280, + Ht = 1024, + Depth = 16, /* bits per pixel */ + + Pcolours = 256, /* Palette */ + Pred = 0, + Pgreen = 1, + Pblue = 2, + + Pblack = 0x00, + Pwhite = 0xFF, + + /* settings indices */ + Res800x600 = 0, + Res1024x768, + Res1280x1024, + Res1400x1050, +}; + +struct Settings { + uint wid; /* width in pixels */ + uint ht; /* height in pixels */ + uint freq; /* refresh frequency; only printed */ + uint chan; /* draw chan */ + + /* shouldn't be needed? */ + uint pixelclock; + + /* horizontal timing */ + uint hbp; /* back porch: pixel clocks before scan line */ + uint hfp; /* front porch: pixel clocks after scan line */ + uint hsw; /* sync pulse width: more hfp */ + + /* vertical timing */ + uint vbp; /* back porch: line clocks before frame */ + uint vfp; /* front porch: line clocks after frame */ + uint vsw; /* sync pulse width: more vfp */ +}; + +struct OScreen { + Lock; + Cursor; + Settings *settings; + int open; +}; + +struct Omap3fb { /* frame buffer for 24-bit active color */ +// short palette[256]; + /* pixel data, even; base type's width must match Depth */ + ushort pixel[Wid*Ht]; +}; diff --git a/sys/src/9/omap/softfpu.c b/sys/src/9/omap/softfpu.c new file mode 100755 index 000000000..7f01446c0 --- /dev/null +++ b/sys/src/9/omap/softfpu.c @@ -0,0 +1,119 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +int +fpudevprocio(Proc* proc, void* a, long n, uintptr offset, int write) +{ + /* + * Called from procdevtab.read and procdevtab.write + * allow user process access to the FPU registers. + * This is the only FPU routine which is called directly + * from the port code; it would be nice to have dynamic + * creation of entries in the device file trees... + */ + USED(proc, a, n, offset, write); + + return 0; +} + +void +fpunotify(Ureg*) +{ + /* + * 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 +fpunoted(void) +{ + /* + * Called from sysnoted() via the machine-dependent + * noted() routine. + * Clear the flag set above in fpunotify(). + */ +} + +void +fpusysrfork(Ureg*) +{ + /* + * 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 +fpusysrforkchild(Proc*, Ureg *, Proc*) +{ + /* + * Called later in sysrfork() via the machine-dependent + * sysrforkchild() routine. + * Copy the parent FPU state to the child. + */ +} + +void +fpuprocsave(Proc*) +{ + /* + * 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 +fpuprocrestore(Proc*) +{ + /* + * 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 +fpusysprocsetup(Proc*) +{ + /* + * Disable the FPU. + * Called from sysexec() via sysprocsetup() to + * set the FPU for the new process. + */ +} + +void +fpuinit(void) +{ +} + +int +fpuemu(Ureg* ureg) +{ + int nfp; + + if(waserror()){ + splhi(); + postnote(up, 1, up->errstr, NDebug); + return 1; + } + spllo(); + nfp = fpiarm(ureg); + splhi(); + poperror(); + + return nfp; +} diff --git a/sys/src/9/omap/syscall.c b/sys/src/9/omap/syscall.c new file mode 100755 index 000000000..f9f390e2b --- /dev/null +++ b/sys/src/9/omap/syscall.c @@ -0,0 +1,333 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "../port/systab.h" + +#include +#include "ureg.h" + +#include "arm.h" + +typedef struct { + uintptr ip; + Ureg* arg0; + char* arg1; + char msg[ERRMAX]; + Ureg* old; + Ureg ureg; +} NFrame; + +/* + * Return user to state before notify() + */ +static void +noted(Ureg* cur, uintptr arg0) +{ + NFrame *nf; + Ureg *nur; + + qlock(&up->debug); + if(arg0 != NRSTR && !up->notified){ + qunlock(&up->debug); + pprint("call to noted() when not notified\n"); + pexit("Suicide", 0); + } + up->notified = 0; + fpunoted(); + + nf = up->ureg; + + /* sanity clause */ + if(!okaddr(PTR2UINT(nf), sizeof(NFrame), 0)){ + qunlock(&up->debug); + pprint("bad ureg in noted %#p\n", nf); + pexit("Suicide", 0); + } + + /* don't let user change system flags */ + nur = &nf->ureg; + nur->psr &= PsrMask|PsrDfiq|PsrDirq; + nur->psr |= (cur->psr & ~(PsrMask|PsrDfiq|PsrDirq)); + + memmove(cur, nur, sizeof(Ureg)); + + switch((int)arg0){ + case NCONT: + case NRSTR: + if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->sp, BY2WD, 0)){ + qunlock(&up->debug); + pprint("suicide: trap in noted\n"); + pexit("Suicide", 0); + } + up->ureg = nf->old; + qunlock(&up->debug); + break; + case NSAVE: + if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->sp, BY2WD, 0)){ + qunlock(&up->debug); + pprint("suicide: trap in noted\n"); + pexit("Suicide", 0); + } + qunlock(&up->debug); + + splhi(); + nf->arg1 = nf->msg; + nf->arg0 = &nf->ureg; + nf->ip = 0; + cur->sp = PTR2UINT(nf); + break; + default: + pprint("unknown noted arg %#p\n", arg0); + up->lastnote.flag = NDebug; + /*FALLTHROUGH*/ + case NDFLT: + if(up->lastnote.flag == NDebug){ + qunlock(&up->debug); + pprint("suicide: %s\n", up->lastnote.msg); + } + else + qunlock(&up->debug); + pexit(up->lastnote.msg, up->lastnote.flag != NDebug); + } +} + +/* + * Call user, if necessary, with note. + * Pass user the Ureg struct and the note on his stack. + */ +int +notify(Ureg* ureg) +{ + int l; + Note *n; + u32int s; + uintptr sp; + NFrame *nf; + + if(up->procctl) + procctl(up); + if(up->nnote == 0) + return 0; + + fpunotify(ureg); + + s = spllo(); + qlock(&up->debug); + + up->notepending = 0; + n = &up->note[0]; + if(strncmp(n->msg, "sys:", 4) == 0){ + l = strlen(n->msg); + if(l > ERRMAX-23) /* " pc=0x0123456789abcdef\0" */ + l = ERRMAX-23; + snprint(n->msg + l, sizeof n->msg - l, " pc=%#lux", ureg->pc); + } + + if(n->flag != NUser && (up->notified || up->notify == 0)){ + if(n->flag == NDebug) + pprint("suicide: %s\n", n->msg); + qunlock(&up->debug); + pexit(n->msg, n->flag != NDebug); + } + + if(up->notified){ + qunlock(&up->debug); + splhi(); + return 0; + } + + if(up->notify == nil){ + qunlock(&up->debug); + pexit(n->msg, n->flag != NDebug); + } + if(!okaddr(PTR2UINT(up->notify), 1, 0)){ + pprint("suicide: notify function address %#p\n", up->notify); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + sp = ureg->sp - sizeof(NFrame); + if(!okaddr(sp, sizeof(NFrame), 1)){ + qunlock(&up->debug); + pprint("suicide: notify stack address %#p\n", sp); + pexit("Suicide", 0); + } + + nf = UINT2PTR(sp); + memmove(&nf->ureg, ureg, sizeof(Ureg)); + nf->old = up->ureg; + up->ureg = nf; + memmove(nf->msg, up->note[0].msg, ERRMAX); + nf->arg1 = nf->msg; + nf->arg0 = &nf->ureg; + nf->ip = 0; + + ureg->sp = sp; + ureg->pc = PTR2UINT(up->notify); + up->notified = 1; + up->nnote--; + memmove(&up->lastnote, &up->note[0], sizeof(Note)); + memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note)); + + qunlock(&up->debug); + splx(s); + + return 1; +} + +void +syscall(Ureg* ureg) +{ + char *e; + u32int s; + ulong sp; + long ret; + int i, scallnr; + + if(!userureg(ureg)) + panic("syscall: from kernel: pc %#lux r14 %#lux psr %#lux", + ureg->pc, ureg->r14, ureg->psr); + + cycles(&up->kentry); + + m->syscall++; + up->insyscall = 1; + up->pc = ureg->pc; + up->dbgreg = ureg; + + if(up->procctl == Proc_tracesyscall){ + up->procctl = Proc_stopme; + procctl(up); + } + + scallnr = ureg->r0; + up->scallnr = scallnr; + if(scallnr == RFORK) + fpusysrfork(ureg); + spllo(); + + sp = ureg->sp; + up->nerrlab = 0; + ret = -1; + if(!waserror()){ + if(scallnr >= nsyscall){ + pprint("bad sys call number %d pc %#lux\n", + scallnr, ureg->pc); + postnote(up, 1, "sys: bad sys call", NDebug); + error(Ebadarg); + } + + if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD)) + validaddr(sp, sizeof(Sargs)+BY2WD, 0); + + up->s = *((Sargs*)(sp+BY2WD)); + up->psstate = sysctab[scallnr]; + + /* iprint("%s: syscall %s\n", up->text, sysctab[scallnr]?sysctab[scallnr]:"huh?"); */ + + ret = systab[scallnr](up->s.args); + poperror(); + }else{ + /* failure: save the error buffer for errstr */ + e = up->syserrstr; + up->syserrstr = up->errstr; + up->errstr = e; + } + if(up->nerrlab){ + print("bad errstack [%d]: %d extra\n", scallnr, up->nerrlab); + for(i = 0; i < NERR; i++) + print("sp=%#p pc=%#p\n", + up->errlab[i].sp, up->errlab[i].pc); + panic("error stack"); + } + + /* + * Put return value in frame. On the x86 the syscall is + * just another trap and the return value from syscall is + * ignored. On other machines the return value is put into + * the results register by caller of syscall. + */ + ureg->r0 = ret; + + if(up->procctl == Proc_tracesyscall){ + up->procctl = Proc_stopme; + s = splhi(); + procctl(up); + splx(s); + } + + up->insyscall = 0; + up->psstate = 0; + + if(scallnr == NOTED) + noted(ureg, *(ulong*)(sp+BY2WD)); + + splhi(); + if(scallnr != RFORK && (up->procctl || up->nnote)) + notify(ureg); + + /* if we delayed sched because we held a lock, sched now */ + if(up->delaysched){ + sched(); + splhi(); + } + kexit(ureg); +} + +long /* void* */ +execregs(ulong entry, ulong ssize, ulong nargs) +{ + ulong *sp; + Ureg *ureg; + + sp = (ulong*)(USTKTOP - ssize); + *--sp = nargs; + + ureg = up->dbgreg; +// memset(ureg, 0, 15*sizeof(ulong)); + ureg->r13 = (ulong)sp; + ureg->pc = entry; +//print("%lud: EXECREGS pc %#ux sp %#ux nargs %ld\n", up->pid, ureg->pc, ureg->r13, nargs); + + /* + * return the address of kernel/user shared data + * (e.g. clock stuff) + */ + return USTKTOP-sizeof(Tos); +} + +void +sysprocsetup(Proc* p) +{ + fpusysprocsetup(p); +} + +/* + * Craft a return frame which will cause the child to pop out of + * the scheduler in user mode with the return register zero. Set + * pc to point to a l.s return function. + */ +void +forkchild(Proc *p, Ureg *ureg) +{ + Ureg *cureg; + +//print("%lud setting up for forking child %lud\n", up->pid, p->pid); + p->sched.sp = (ulong)p->kstack+KSTACK-sizeof(Ureg); + p->sched.pc = (ulong)forkret; + + cureg = (Ureg*)(p->sched.sp); + memmove(cureg, ureg, sizeof(Ureg)); + + /* syscall returns 0 for child */ + cureg->r0 = 0; + + /* Things from bottom of syscall which were never executed */ + p->psstate = 0; + p->insyscall = 0; + + fpusysrforkchild(p, cureg, up); +} diff --git a/sys/src/9/omap/trap.c b/sys/src/9/omap/trap.c new file mode 100755 index 000000000..6b0911e1b --- /dev/null +++ b/sys/src/9/omap/trap.c @@ -0,0 +1,769 @@ +/* + * omap3530 traps, exceptions, interrupts, system calls. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ureg.h" +#include "arm.h" + +enum { + Nirqs = 96, + Nvec = 8, /* # of vectors at start of lexception.s */ + Bi2long = BI2BY * sizeof(long), +}; + +extern int notify(Ureg*); + +extern int ldrexvalid; + +/* omap35x intc (aka mpu_intc) */ +typedef struct Intrregs Intrregs; +struct Intrregs { + /* + * the manual inserts "INTCPS_" before each register name; + * we'll just assume the prefix. + */ + uchar _pad0[4*4]; + ulong sysconfig; + ulong sysstatus; /* ro */ + uchar _pad1[0x40 - 0x18]; + ulong sir_irq; /* ro */ + ulong sir_fiq; /* ro */ + ulong control; + ulong protection; + ulong idle; + uchar _pad2[0x60 - 0x54]; + ulong irq_priority; + ulong fiq_priority; + ulong threshold; + uchar _pad3[0x80 - 0x6c]; + struct Bits { /* bitmaps */ + ulong itr; /* ro: pending intrs (no mask) */ + ulong mir; /* interrupt mask: 1 means masked */ + ulong mir_clear; /* wo: 1 sets the bit */ + ulong mir_set; /* wo: 1 clears the bit */ + ulong isr_set; /* software interrupts */ + ulong isr_clear; /* wo */ + ulong pending_irq; /* ro */ + ulong pending_fiq; /* ro */ + } bits[3]; /* 3*32 = 96 (Nirqs) */ + ulong ilr[Nirqs]; +}; + +enum { + /* sysconfig bits */ + Softreset = 1<<1, + + /* sysstatus bits */ + Resetdone = 1<<0, + + /* sir_irq/fiq bits */ + Activeirq = MASK(7), + + /* control bits */ + Newirqagr = 1<<0, + + /* protection bits */ + Protection = 1<<0, + + /* irq/fiq_priority bits */ + Irqpriority = MASK(6), + + /* threshold bits */ + Prioritythreshold = MASK(8), + + /* ilr bits */ + Priority = MASK(8) - MASK(2), +}; + +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; +static Vpage0 *vpage0; + +uvlong ninterrupt; +uvlong ninterruptticks; +int irqtooearly = 1; + +static volatile int probing, trapped; + +static int +irqinuse(uint irq) +{ + Intrregs *ip = (Intrregs *)PHYSINTC; + + /* + * mir registers are odd: a 0 bit means intr unmasked (i.e., + * we've unmasked it because it's in use). + */ + return (ip->bits[irq / Bi2long].mir & (1 << (irq % Bi2long))) == 0; +} + +static void +intcmask(uint irq) +{ + Intrregs *ip = (Intrregs *)PHYSINTC; + + ip->bits[irq / Bi2long].mir_set = 1 << (irq % Bi2long); + coherence(); +} + +static void +intcunmask(uint irq) +{ + Intrregs *ip = (Intrregs *)PHYSINTC; + + ip->bits[irq / Bi2long].mir_clear = 1 << (irq % Bi2long); + coherence(); +} + +static void +intcmaskall(void) +{ + int i; + Intrregs *ip = (Intrregs *)PHYSINTC; + + for (i = 0; i < 3; i++) + ip->bits[i].mir_set = ~0; + coherence(); +} + +static void +intcunmaskall(void) +{ + int i; + Intrregs *ip = (Intrregs *)PHYSINTC; + + for (i = 0; i < 3; i++) + ip->bits[i].mir_clear = ~0; + coherence(); +} + +static void +intcinvertall(void) +{ + int i, s; + ulong bits; + Intrregs *ip = (Intrregs *)PHYSINTC; + + s = splhi(); + for (i = 0; i < 3; i++) { + bits = ip->bits[i].mir; + ip->bits[i].mir_set = ~0; /* mask all */ + coherence(); + /* clearing enables only those intrs. that were disabled */ + ip->bits[i].mir_clear = bits; + } + coherence(); + splx(s); +} + +static void +intrsave(ulong buf[3]) +{ + int i; + Intrregs *ip = (Intrregs *)PHYSINTC; + + for (i = 0; i < nelem(buf); i++) + buf[i] = ip->bits[i].mir; + coherence(); +} + +static void +intrrestore(ulong buf[3]) +{ + int i, s; + Intrregs *ip = (Intrregs *)PHYSINTC; + + s = splhi(); + for (i = 0; i < nelem(buf); i++) { + ip->bits[i].mir_clear = ~0; /* unmask all */ + coherence(); + ip->bits[i].mir_set = buf[i]; /* mask previously disabled */ + } + coherence(); + splx(s); +} + +/* + * set up for exceptions + */ +void +trapinit(void) +{ + int i; + Intrregs *ip = (Intrregs *)PHYSINTC; + + /* 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, m->sfiq); + setr13(PsrMirq, m->sirq); + setr13(PsrMabt, m->sabt); + setr13(PsrMund, m->sund); +#ifdef HIGH_SECURITY + setr13(PsrMmon, m->smon); +#endif + setr13(PsrMsys, m->ssys); + + intcmaskall(); + ip->control = 0; + ip->threshold = Prioritythreshold; /* disable threshold */ + for (i = 0; i < Nirqs; i++) + ip->ilr[i] = 0<<2 | 0; /* all intrs pri 0 & to irq, not fiq */ + irqtooearly = 0; + coherence(); +} + +void +intrsoff(void) +{ + Intrregs *ip = (Intrregs *)PHYSINTC; + + intcmaskall(); + ip->control = Newirqagr; /* dismiss interrupt */ + coherence(); +} + +/* + * enable an irq interrupt + */ +int +irqenable(int irq, void (*f)(Ureg*, void*), void* a, char *name) +{ + Vctl *v; + + if(irq >= nelem(vctl) || irq < 0) + panic("irqenable irq %d", irq); + + if (irqtooearly) { + iprint("irqenable for %d %s called too early\n", irq, name); + return -1; + } + if(irqinuse(irq)) + print("irqenable: %s: irq %d already in use, chaining\n", + name, irq); + 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]; + vctl[irq] = v; + + intcunmask(irq); + unlock(&vctllock); + return 0; +} + +/* + * disable an irq interrupt + */ +int +irqdisable(int irq, void (*f)(Ureg*, void*), void* a, char *name) +{ + Vctl **vp, *v; + + if(irq >= nelem(vctl) || irq < 0) + panic("irqdisable irq %d", irq); + + lock(&vctllock); + for(vp = &vctl[irq]; v = *vp; vp = &v->next) + if (v->f == f && v->a == a && strcmp(v->name, name) == 0){ + print("irqdisable: remove %s\n", name); + *vp = v->next; + free(v); + break; + } + + if(v == nil) + print("irqdisable: irq %d, name %s not enabled\n", irq, name); + if(vctl[irq] == nil){ + print("irqdisable: clear icmr bit %d\n", irq); + intcmask(irq); + } + unlock(&vctllock); + + return 0; +} + +/* + * called by trap to handle access faults + */ +static void +faultarm(Ureg *ureg, uintptr va, int user, int read) +{ + int n, insyscall; + char buf[ERRMAX]; + + if(up == nil) { + dumpregs(ureg); + panic("fault: nil up in faultarm, accessing %#p", va); + } + insyscall = up->insyscall; + up->insyscall = 1; + n = fault(va, read); + if(n < 0){ + if(!user){ + dumpregs(ureg); + panic("fault: kernel accessing %#p", va); + } + /* don't dump registers; programs suicide all the time */ + snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p", + read? "read": "write", va); + postnote(up, 1, buf, NDebug); + } + up->insyscall = insyscall; +} + +/* + * called by trap to handle interrupts. + * returns true iff a clock interrupt, thus maybe reschedule. + */ +static int +irq(Ureg* ureg) +{ + int clockintr; + uint irqno, handled, t, ticks = perfticks(); + Intrregs *ip = (Intrregs *)PHYSINTC; + Vctl *v; + static int nesting, lastirq = -1; + + handled = 0; + irqno = ip->sir_irq & Activeirq; + + if (irqno >= 37 && irqno <= 47) /* this is a clock intr? */ + m->inclockintr++; /* yes, count nesting */ + lastirq = irqno; + + if (irqno >= nelem(vctl)) { + iprint("trap: irq %d >= # vectors (%d)\n", irqno, nelem(vctl)); + ip->control = Newirqagr; /* dismiss interrupt */ + return 0; + } + + ++nesting; + for(v = vctl[irqno]; v != nil; v = v->next) + if (v->f) { + if (islo()) + panic("trap: pl0 before trap handler for %s", + v->name); + v->f(ureg, v->a); + if (islo()) + panic("trap: %s lowered pl", v->name); +// splhi(); /* in case v->f lowered pl */ + handled++; + } + if(!handled) { + iprint("unexpected interrupt: irq %d", irqno); + switch (irqno) { + case 56: + case 57: + iprint(" (I⁲C)"); + break; + case 83: + case 86: + case 94: + iprint(" (MMC)"); + break; + } + + if(irqno < nelem(vctl)) { + intcmask(irqno); + iprint(", now masked"); + } + iprint("\n"); + } + t = perfticks(); + ninterrupt++; + if(t < ticks) + ninterruptticks += ticks-t; + else + ninterruptticks += t-ticks; + ip->control = Newirqagr; /* dismiss interrupt */ + coherence(); + + --nesting; + clockintr = m->inclockintr == 1; + if (irqno >= 37 && irqno <= 47) + m->inclockintr--; + return clockintr; +} + +/* + * 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; +} + +void prgpmcerrs(void); + +/* + * here on all exceptions other than syscall (SWI) + */ +void +trap(Ureg *ureg) +{ + int clockintr, user, x, rv, rem; + ulong inst, fsr; + uintptr va; + char buf[ERRMAX]; + + 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 at pc %#lux\n", + rem, up, ureg, ureg->pc); + delay(1000); + dumpstack(); + panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux", + rem, up, ureg, ureg->pc); + } + + user = (ureg->psr & PsrMask) == PsrMusr; + if(user){ + up->dbgreg = ureg; + cycles(&up->kentry); + } + + /* + * All interrupts/exceptions should be resumed at ureg->pc-4, + * except for Data Abort which resumes at ureg->pc-8. + */ + if(ureg->type == (PsrMabt+1)) + ureg->pc -= 8; + else + ureg->pc -= 4; + + clockintr = 0; /* if set, may call sched() before return */ + switch(ureg->type){ + default: + panic("unknown trap; type %#lux, psr mode %#lux", ureg->type, + ureg->psr & PsrMask); + break; + case PsrMirq: + ldrexvalid = 0; + clockintr = irq(ureg); + m->intr++; + break; + case PsrMabt: /* prefetch fault */ + ldrexvalid = 0; + faultarm(ureg, ureg->pc, user, 1); + break; + case PsrMabt+1: /* data fault */ + ldrexvalid = 0; + 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; + if (probing && !user) { + if (trapped++ > 0) + panic("trap: recursive probe %#lux", va); + ureg->pc += 4; /* continue at next instruction */ + break; + } + switch(fsr){ + default: + case 0xa: /* ? was under external abort */ + panic("unknown data fault, 6b fsr %#lux", fsr); + break; + case 0x0: + panic("vector exception at %#lux", ureg->pc); + break; + case 0x1: /* alignment fault */ + case 0x3: /* access flag fault (section) */ + if(user){ + snprint(buf, sizeof buf, + "sys: alignment: pc %#lux va %#p\n", + ureg->pc, va); + postnote(up, 1, buf, NDebug); + } else + panic("kernel alignment: pc %#lux va %#p", ureg->pc, va); + break; + case 0x2: + panic("terminal exception at %#lux", ureg->pc); + break; + case 0x4: /* icache maint fault */ + case 0x6: /* access flag fault (page) */ + case 0x8: /* precise external abort, non-xlat'n */ + case 0x28: + case 0xc: /* l1 translation, precise ext. abort */ + case 0x2c: + case 0xe: /* l2 translation, precise ext. abort */ + case 0x2e: + case 0x16: /* imprecise ext. abort, non-xlt'n */ + case 0x36: + panic("external abort %#lux pc %#lux addr %#p", + fsr, ureg->pc, va); + break; + case 0x1c: /* l1 translation, precise parity err */ + case 0x1e: /* l2 translation, precise parity err */ + case 0x18: /* imprecise parity or ecc err */ + panic("translation parity error %#lux pc %#lux addr %#p", + fsr, ureg->pc, va); + break; + case 0x5: /* translation fault, no section entry */ + case 0x7: /* translation fault, no page entry */ + faultarm(ureg, va, user, !writetomem(inst)); + break; + case 0x9: + case 0xb: + /* domain fault, accessing something we shouldn't */ + if(user){ + snprint(buf, sizeof buf, + "sys: access violation: pc %#lux va %#p\n", + ureg->pc, va); + postnote(up, 1, buf, NDebug); + } else + panic("kernel access violation: pc %#lux va %#p", + ureg->pc, va); + break; + case 0xd: + case 0xf: + /* permission error, copy on write or real permission error */ + faultarm(ureg, va, user, !writetomem(inst)); + break; + } + break; + case PsrMund: /* undefined instruction */ + if(user){ + /* look for floating point instructions to interpret */ + x = spllo(); + rv = fpiarm(ureg); + splx(x); + if(rv == 0){ + ldrexvalid = 0; + snprint(buf, sizeof buf, + "undefined instruction: pc %#lux\n", + ureg->pc); + postnote(up, 1, buf, NDebug); + } + }else{ + if (ureg->pc & 3) { + iprint("rounding fault pc %#lux down to word\n", + ureg->pc); + ureg->pc &= ~3; + } + iprint("undefined instruction: pc %#lux inst %#ux\n", + ureg->pc, ((u32int*)ureg->pc)[-2]); + panic("undefined instruction"); + } + break; + } + splhi(); + + /* delaysched set because we held a lock or because our quantum ended */ + if(up && up->delaysched && clockintr){ + ldrexvalid = 0; + 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; + + 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; + } + 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"); +} + +void +dumpstack(void) +{ + callwithureg(dumpstackwithureg); +} + +/* + * dump system control coprocessor registers + */ +static void +dumpscr(void) +{ + iprint("0:\t%#8.8ux id\n", cpidget()); + iprint("\t%8.8#ux ct\n", cpctget()); + iprint("1:\t%#8.8ux control\n", controlget()); + iprint("2:\t%#8.8ux ttb\n", ttbget()); + iprint("3:\t%#8.8ux dac\n", dacget()); + iprint("4:\t(reserved)\n"); + iprint("5:\t%#8.8ux fsr\n", fsrget()); + iprint("6:\t%#8.8ux far\n", farget()); + iprint("7:\twrite-only cache\n"); + iprint("8:\twrite-only tlb\n"); + iprint("13:\t%#8.8ux pid\n", pidget()); + delay(10); +} + +/* + * 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(20); +} + +void +dumpregs(Ureg* ureg) +{ + dumpgpr(ureg); + dumpscr(); +} + +vlong +probeaddr(uintptr addr) +{ + vlong v; + static Lock fltlck; + + ilock(&fltlck); + trapped = 0; + probing = 1; + coherence(); + + v = *(ulong *)addr; /* this may cause a fault */ + USED(probing); + coherence(); + + probing = 0; + coherence(); + if (trapped) + v = -1; + iunlock(&fltlck); + return v; +} diff --git a/sys/src/9/omap/uarti8250.c b/sys/src/9/omap/uarti8250.c new file mode 100755 index 000000000..7642a2685 --- /dev/null +++ b/sys/src/9/omap/uarti8250.c @@ -0,0 +1,850 @@ +/* + * omap35 8250-like UART + * + * we ignore the first 2 uarts on the omap35 (see below) and use the + * third one but call it 0. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +enum { /* registers */ + Rbr = 0, /* Receiver Buffer (RO) */ + Thr = 0, /* Transmitter Holding (WO) */ + Ier = 1, /* Interrupt Enable */ + Iir = 2, /* Interrupt Identification (RO) */ + Fcr = 2, /* FIFO Control (WO) */ + Lcr = 3, /* Line Control */ + Mcr = 4, /* Modem Control */ + Lsr = 5, /* Line Status */ + Msr = 6, /* Modem Status */ + Scr = 7, /* Scratch Pad */ + Mdr = 8, /* Mode Def'n (omap rw) */ +// Usr = 31, /* Uart Status Register; missing in omap? */ + Dll = 0, /* Divisor Latch LSB */ + Dlm = 1, /* Divisor Latch MSB */ +}; + +enum { /* Usr */ + Busy = 0x01, +}; + +enum { /* Ier */ + Erda = 0x01, /* Enable Received Data Available */ + Ethre = 0x02, /* Enable Thr Empty */ + Erls = 0x04, /* Enable Receiver Line Status */ + Ems = 0x08, /* Enable Modem Status */ +}; + +enum { /* Iir */ + Ims = 0x00, /* Ms interrupt */ + Ip = 0x01, /* Interrupt Pending (not) */ + Ithre = 0x02, /* Thr Empty */ + Irda = 0x04, /* Received Data Available */ + Irls = 0x06, /* Receiver Line Status */ + Ictoi = 0x0C, /* Character Time-out Indication */ + IirMASK = 0x3F, + Ifena = 0xC0, /* FIFOs enabled */ +}; + +enum { /* Fcr */ + FIFOena = 0x01, /* FIFO enable */ + FIFOrclr = 0x02, /* clear Rx FIFO */ + FIFOtclr = 0x04, /* clear Tx FIFO */ +// FIFOdma = 0x08, + FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */ + FIFO4 = 0x40, /* 4 bytes */ + FIFO8 = 0x80, /* 8 bytes */ + FIFO14 = 0xC0, /* 14 bytes */ +}; + +enum { /* Lcr */ + Wls5 = 0x00, /* Word Length Select 5 bits/byte */ + Wls6 = 0x01, /* 6 bits/byte */ + Wls7 = 0x02, /* 7 bits/byte */ + Wls8 = 0x03, /* 8 bits/byte */ + WlsMASK = 0x03, + Stb = 0x04, /* 2 stop bits */ + Pen = 0x08, /* Parity Enable */ + Eps = 0x10, /* Even Parity Select */ + Stp = 0x20, /* Stick Parity */ + Brk = 0x40, /* Break */ + Dlab = 0x80, /* Divisor Latch Access Bit */ +}; + +enum { /* Mcr */ + Dtr = 0x01, /* Data Terminal Ready */ + Rts = 0x02, /* Ready To Send */ + Out1 = 0x04, /* no longer in use */ +// Ie = 0x08, /* IRQ Enable (cd_sts_ch on omap) */ + Dm = 0x10, /* Diagnostic Mode loopback */ +}; + +enum { /* Lsr */ + Dr = 0x01, /* Data Ready */ + Oe = 0x02, /* Overrun Error */ + Pe = 0x04, /* Parity Error */ + Fe = 0x08, /* Framing Error */ + Bi = 0x10, /* Break Interrupt */ + Thre = 0x20, /* Thr Empty */ + Temt = 0x40, /* Transmitter Empty */ + FIFOerr = 0x80, /* error in receiver FIFO */ +}; + +enum { /* Msr */ + Dcts = 0x01, /* Delta Cts */ + Ddsr = 0x02, /* Delta Dsr */ + Teri = 0x04, /* Trailing Edge of Ri */ + Ddcd = 0x08, /* Delta Dcd */ + Cts = 0x10, /* Clear To Send */ + Dsr = 0x20, /* Data Set Ready */ + Ri = 0x40, /* Ring Indicator */ + Dcd = 0x80, /* Carrier Detect */ +}; + +enum { /* Mdr */ + Modemask = 7, + Modeuart = 0, +}; + + +typedef struct Ctlr { + u32int* io; + int irq; + int tbdf; + int iena; + int poll; + + uchar sticky[Scr+1]; + + Lock; + int hasfifo; + int checkfifo; + int fena; +} Ctlr; + +extern PhysUart i8250physuart; + +static Ctlr i8250ctlr[] = { +{ .io = (u32int*)PHYSCONS, /* UART3 in TI terminology */ + .irq = 74, + .tbdf = -1, + .poll = 0, }, + +/* these exist, but I don't think they're connected to anything external */ +//{ .io = (u32int*)PHYSUART0, +// .irq = 72, +// .tbdf = -1, +// .poll = 0, }, +// +//{ .io = (u32int*)PHYSUART1, +// .irq = 73, +// .tbdf = -1, +// .poll = 0, }, +}; + +static Uart i8250uart[] = { +{ .regs = &i8250ctlr[0], /* not [2] */ + .name = "COM3", + .freq = 3686000, /* Not used, we use the global i8250freq */ + .phys = &i8250physuart, + .console= 1, + .next = nil, }, + +/* these exist, but I don't think they're connected to anything external */ +//{ .regs = &i8250ctlr[0], +// .name = "COM1", +// .freq = 3686000, /* Not used, we use the global i8250freq */ +// .phys = &i8250physuart, +// .special= 0, +// .next = &i8250uart[1], }, +// +//{ .regs = &i8250ctlr[1], +// .name = "COM2", +// .freq = 3686000, /* Not used, we use the global i8250freq */ +// .phys = &i8250physuart, +// .special= 0, +// .next = &i8250uart[2], }, +}; + +#define csr8r(c, r) ((c)->io[r]) +#define csr8w(c, r, v) ((c)->io[r] = (c)->sticky[r] | (v), coherence()) +#define csr8o(c, r, v) ((c)->io[r] = (v), coherence()) + +static long +i8250status(Uart* uart, void* buf, long n, long offset) +{ + char *p; + Ctlr *ctlr; + uchar ier, lcr, mcr, msr; + + ctlr = uart->regs; + p = malloc(READSTR); + mcr = ctlr->sticky[Mcr]; + msr = csr8r(ctlr, Msr); + ier = ctlr->sticky[Ier]; + lcr = ctlr->sticky[Lcr]; + snprint(p, READSTR, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n" + "dev(%d) type(%d) framing(%d) overruns(%d) " + "berr(%d) serr(%d)%s%s%s%s\n", + + uart->baud, + uart->hup_dcd, + (msr & Dsr) != 0, + uart->hup_dsr, + (lcr & WlsMASK) + 5, + (ier & Ems) != 0, + (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n', + (mcr & Rts) != 0, + (lcr & Stb) ? 2: 1, + ctlr->fena, + + uart->dev, + uart->type, + uart->ferr, + uart->oerr, + uart->berr, + uart->serr, + (msr & Cts) ? " cts": "", + (msr & Dsr) ? " dsr": "", + (msr & Dcd) ? " dcd": "", + (msr & Ri) ? " ring": "" + ); + n = readstr(offset, buf, n, p); + free(p); + + return n; +} + +static void +i8250fifo(Uart* uart, int level) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + if(ctlr->hasfifo == 0) + return; + + /* + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + */ + ilock(ctlr); + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + + /* + * Set the trigger level, default is the max. + * value. + * Some UARTs require FIFOena to be set before + * other bits can take effect, so set it twice. + */ + ctlr->fena = level; + switch(level){ + case 0: + break; + case 1: + level = FIFO1|FIFOena; + break; + case 4: + level = FIFO4|FIFOena; + break; + case 8: + level = FIFO8|FIFOena; + break; + default: + level = FIFO14|FIFOena; + break; + } + csr8w(ctlr, Fcr, level); + csr8w(ctlr, Fcr, level); + iunlock(ctlr); +} + +static void +i8250dtr(Uart* uart, int on) +{ + Ctlr *ctlr; + + /* + * Toggle DTR. + */ + ctlr = uart->regs; + if(on) + ctlr->sticky[Mcr] |= Dtr; + else + ctlr->sticky[Mcr] &= ~Dtr; + csr8w(ctlr, Mcr, 0); +} + +static void +i8250rts(Uart* uart, int on) +{ + Ctlr *ctlr; + + /* + * Toggle RTS. + */ + ctlr = uart->regs; + if(on) + ctlr->sticky[Mcr] |= Rts; + else + ctlr->sticky[Mcr] &= ~Rts; + csr8w(ctlr, Mcr, 0); +} + +static void +i8250modemctl(Uart* uart, int on) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + ilock(&uart->tlock); + if(on){ + ctlr->sticky[Ier] |= Ems; + csr8w(ctlr, Ier, 0); + uart->modem = 1; + uart->cts = csr8r(ctlr, Msr) & Cts; + } + else{ + ctlr->sticky[Ier] &= ~Ems; + csr8w(ctlr, Ier, 0); + uart->modem = 0; + uart->cts = 1; + } + iunlock(&uart->tlock); + + /* modem needs fifo */ + (*uart->phys->fifo)(uart, on); +} + +static int +i8250parity(Uart* uart, int parity) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~(Eps|Pen); + + switch(parity){ + case 'e': + lcr |= Eps|Pen; + break; + case 'o': + lcr |= Pen; + break; + case 'n': + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->parity = parity; + + return 0; +} + +static int +i8250stop(Uart* uart, int stop) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~Stb; + + switch(stop){ + case 1: + break; + case 2: + lcr |= Stb; + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->stop = stop; + + return 0; +} + +static int +i8250bits(Uart* uart, int bits) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~WlsMASK; + + switch(bits){ + case 5: + lcr |= Wls5; + break; + case 6: + lcr |= Wls6; + break; + case 7: + lcr |= Wls7; + break; + case 8: + lcr |= Wls8; + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->bits = bits; + + return 0; +} + +static int +i8250baud(Uart* uart, int baud) +{ +#ifdef notdef /* don't change the speed */ + ulong bgc; + Ctlr *ctlr; + extern int i8250freq; /* In the config file */ + + /* + * Set the Baud rate by calculating and setting the Baud rate + * Generator Constant. This will work with fairly non-standard + * Baud rates. + */ + if(i8250freq == 0 || baud <= 0) + return -1; + bgc = (i8250freq+8*baud-1)/(16*baud); + + ctlr = uart->regs; + while(csr8r(ctlr, Usr) & Busy) + delay(1); + csr8w(ctlr, Lcr, Dlab); /* begin kludge */ + csr8o(ctlr, Dlm, bgc>>8); + csr8o(ctlr, Dll, bgc); + csr8w(ctlr, Lcr, 0); +#endif + uart->baud = baud; + return 0; +} + +static void +i8250break(Uart* uart, int ms) +{ + Ctlr *ctlr; + + if (up == nil) + panic("i8250break: nil up"); + /* + * Send a break. + */ + if(ms <= 0) + ms = 200; + + ctlr = uart->regs; + csr8w(ctlr, Lcr, Brk); + tsleep(&up->sleep, return0, 0, ms); + csr8w(ctlr, Lcr, 0); +} + +static void +emptyoutstage(Uart *uart, int n) +{ + _uartputs((char *)uart->op, n); + uart->op = uart->oe = uart->ostage; +} + +static void +i8250kick(Uart* uart) +{ + int i; + Ctlr *ctlr; + + if(/* uart->cts == 0 || */ uart->blocked) + return; + + if(!normalprint) { /* early */ + if (uart->op < uart->oe) + emptyoutstage(uart, uart->oe - uart->op); + while ((i = uartstageoutput(uart)) > 0) + emptyoutstage(uart, i); + return; + } + + /* nothing more to send? then disable xmit intr */ + ctlr = uart->regs; + if (uart->op >= uart->oe && qlen(uart->oq) == 0 && + csr8r(ctlr, Lsr) & Temt) { + ctlr->sticky[Ier] &= ~Ethre; + csr8w(ctlr, Ier, 0); + return; + } + + /* + * 128 here is an arbitrary limit to make sure + * we don't stay in this loop too long. If the + * chip's output queue is longer than 128, too + * bad -- presotto + */ + for(i = 0; i < 128; i++){ + if(!(csr8r(ctlr, Lsr) & Thre)) + break; + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + csr8o(ctlr, Thr, *uart->op++); /* start tx */ + ctlr->sticky[Ier] |= Ethre; + csr8w(ctlr, Ier, 0); /* intr when done */ + } +} + +void +serialkick(void) +{ + uartkick(&i8250uart[CONSOLE]); +} + +static void +i8250interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Uart *uart; + int iir, lsr, old, r; + + uart = arg; + ctlr = uart->regs; + for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){ + switch(iir & IirMASK){ + case Ims: /* Ms interrupt */ + r = csr8r(ctlr, Msr); + if(r & Dcts){ + ilock(&uart->tlock); + old = uart->cts; + uart->cts = r & Cts; + if(old == 0 && uart->cts) + uart->ctsbackoff = 2; + iunlock(&uart->tlock); + } + if(r & Ddsr){ + old = r & Dsr; + if(uart->hup_dsr && uart->dsr && !old) + uart->dohup = 1; + uart->dsr = old; + } + if(r & Ddcd){ + old = r & Dcd; + if(uart->hup_dcd && uart->dcd && !old) + uart->dohup = 1; + uart->dcd = old; + } + break; + case Ithre: /* Thr Empty */ + uartkick(uart); + break; + case Irda: /* Received Data Available */ + case Irls: /* Receiver Line Status */ + case Ictoi: /* Character Time-out Indication */ + /* + * Consume any received data. + * If the received byte came in with a break, + * parity or framing error, throw it away; + * overrun is an indication that something has + * already been tossed. + */ + while((lsr = csr8r(ctlr, Lsr)) & Dr){ + if(lsr & (FIFOerr|Oe)) + uart->oerr++; + if(lsr & Pe) + uart->perr++; + if(lsr & Fe) + uart->ferr++; + r = csr8r(ctlr, Rbr); + if(!(lsr & (Bi|Fe|Pe))) + uartrecv(uart, r); + } + break; + + default: + iprint("weird uart interrupt type %#2.2uX\n", iir); + break; + } + } +} + +static void +i8250disable(Uart* uart) +{ + Ctlr *ctlr; + + /* + * Turn off DTR and RTS, disable interrupts and fifos. + */ + (*uart->phys->dtr)(uart, 0); + (*uart->phys->rts)(uart, 0); + (*uart->phys->fifo)(uart, 0); + + ctlr = uart->regs; + ctlr->sticky[Ier] = 0; + csr8w(ctlr, Ier, 0); + + if(ctlr->iena != 0){ + if(irqdisable(ctlr->irq, i8250interrupt, uart, uart->name) == 0) + ctlr->iena = 0; + } +} + +static void +i8250enable(Uart* uart, int ie) +{ + int mode; + Ctlr *ctlr; + + if (up == nil) + return; /* too soon */ + + ctlr = uart->regs; + + /* omap only: set uart/irda/cir mode to uart */ + mode = csr8r(ctlr, Mdr); + csr8o(ctlr, Mdr, (mode & ~Modemask) | Modeuart); + + ctlr->sticky[Lcr] = Wls8; /* no parity */ + csr8w(ctlr, Lcr, 0); + + /* + * Check if there is a FIFO. + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + * Also, reading the Iir outwith i8250interrupt() + * can be dangerous, but this should only happen + * once, before interrupts are enabled. + */ + ilock(ctlr); + if(!ctlr->checkfifo){ + /* + * Wait until the transmitter is really empty. + */ + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + csr8w(ctlr, Fcr, FIFOena); + if(csr8r(ctlr, Iir) & Ifena) + ctlr->hasfifo = 1; + csr8w(ctlr, Fcr, 0); + ctlr->checkfifo = 1; + } + iunlock(ctlr); + + /* + * Enable interrupts and turn on DTR and RTS. + * Be careful if this is called to set up a polled serial line + * early on not to try to enable interrupts as interrupt- + * -enabling mechanisms might not be set up yet. + */ + if(ie){ + if(ctlr->iena == 0 && !ctlr->poll){ + irqenable(ctlr->irq, i8250interrupt, uart, uart->name); + ctlr->iena = 1; + } + ctlr->sticky[Ier] = Erda; +// ctlr->sticky[Mcr] |= Ie; /* not on omap */ + ctlr->sticky[Mcr] = 0; + } + else{ + ctlr->sticky[Ier] = 0; + ctlr->sticky[Mcr] = 0; + } + csr8w(ctlr, Ier, 0); + csr8w(ctlr, Mcr, 0); + + (*uart->phys->dtr)(uart, 1); + (*uart->phys->rts)(uart, 1); + + /* + * During startup, the i8259 interrupt controller is reset. + * This may result in a lost interrupt from the i8250 uart. + * The i8250 thinks the interrupt is still outstanding and does not + * generate any further interrupts. The workaround is to call the + * interrupt handler to clear any pending interrupt events. + * Note: this must be done after setting Ier. + */ + if(ie) + i8250interrupt(nil, uart); +} + +static Uart* +i8250pnp(void) +{ + return i8250uart; +} + +static int +i8250getc(Uart* uart) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + while(!(csr8r(ctlr, Lsr) & Dr)) + delay(1); + return csr8r(ctlr, Rbr); +} + +static void +i8250putc(Uart* uart, int c) +{ + int i; + Ctlr *ctlr; + + if (!normalprint) { /* too early; use brute force */ + int s = splhi(); + + while (!(((ulong *)PHYSCONS)[Lsr] & Thre)) + ; + ((ulong *)PHYSCONS)[Thr] = c; + coherence(); + splx(s); + return; + } + + ctlr = uart->regs; + for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 128; i++) + delay(1); + csr8o(ctlr, Thr, (uchar)c); + for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 128; i++) + delay(1); +} + +void +serialputc(int c) +{ + i8250putc(&i8250uart[CONSOLE], c); +} + +void +serialputs(char* s, int n) +{ + _uartputs(s, n); +} + +#ifdef notdef +static void +i8250poll(Uart* uart) +{ + Ctlr *ctlr; + + /* + * If PhysUart has a non-nil .poll member, this + * routine will be called from the uartclock timer. + * If the Ctlr .poll member is non-zero, when the + * Uart is enabled interrupts will not be enabled + * and the result is polled input and output. + * Not very useful here, but ports to new hardware + * or simulators can use this to get serial I/O + * without setting up the interrupt mechanism. + */ + ctlr = uart->regs; + if(ctlr->iena || !ctlr->poll) + return; + i8250interrupt(nil, uart); +} +#endif + +PhysUart i8250physuart = { + .name = "i8250", + .pnp = i8250pnp, + .enable = i8250enable, + .disable = i8250disable, + .kick = i8250kick, + .dobreak = i8250break, + .baud = i8250baud, + .bits = i8250bits, + .stop = i8250stop, + .parity = i8250parity, + .modemctl = i8250modemctl, + .rts = i8250rts, + .dtr = i8250dtr, + .status = i8250status, + .fifo = i8250fifo, + .getc = i8250getc, + .putc = i8250putc, +// .poll = i8250poll, /* only in 9k, not 9 */ +}; + +static void +i8250dumpregs(Ctlr* ctlr) +{ + int dlm, dll; + int _uartprint(char*, ...); + + csr8w(ctlr, Lcr, Dlab); + dlm = csr8r(ctlr, Dlm); + dll = csr8r(ctlr, Dll); + csr8w(ctlr, Lcr, 0); + + _uartprint("dlm %#ux dll %#ux\n", dlm, dll); +} + +Uart* uartenable(Uart *p); + +/* must call this from a process's context */ +int +i8250console(void) +{ + Uart *uart = &i8250uart[CONSOLE]; + + if (up == nil) + return -1; /* too early */ + + if(uartenable(uart) != nil /* && uart->console */){ + // iprint("i8250console: enabling console uart\n"); + kbdq = uart->iq; + serialoq = uart->oq; + uart->putc = kbdcr2nl; + uart->opens++; + consuart = uart; + } + uartctl(uart, "b115200 l8 pn r1 s1 i1"); + return 0; +} + +void +_uartputs(char* s, int n) +{ + char *e; + + for(e = s+n; s < e; s++){ + if(*s == '\n') + i8250putc(&i8250uart[CONSOLE], '\r'); + i8250putc(&i8250uart[CONSOLE], *s); + } +} + +int +_uartprint(char* fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + _uartputs(buf, n); + + return n; +} diff --git a/sys/src/9/omap/ucalloc.c b/sys/src/9/omap/ucalloc.c new file mode 100755 index 000000000..37e8564c0 --- /dev/null +++ b/sys/src/9/omap/ucalloc.c @@ -0,0 +1,135 @@ +/* + * allocate uncached memory + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include + +typedef struct Private Private; +struct Private { + Lock; + char msg[256]; + char* cur; +}; + +static Private ucprivate; + +static void +ucpoolpanic(Pool* p, char* fmt, ...) +{ + va_list v; + Private *pv; + char msg[sizeof pv->msg]; + + pv = p->private; + va_start(v, fmt); + vseprint(pv->cur, &pv->msg[sizeof pv->msg], fmt, v); + va_end(v); + memmove(msg, pv->msg, sizeof msg); + iunlock(pv); + panic("%s", msg); +} + +static void +ucpoolprint(Pool* p, char* fmt, ...) +{ + va_list v; + Private *pv; + + pv = p->private; + va_start(v, fmt); + pv->cur = vseprint(pv->cur, &pv->msg[sizeof pv->msg], fmt, v); + va_end(v); +} + +static void +ucpoolunlock(Pool* p) +{ + Private *pv; + char msg[sizeof pv->msg]; + + pv = p->private; + if(pv->cur == pv->msg){ + iunlock(pv); + return; + } + + memmove(msg, pv->msg, sizeof msg); + pv->cur = pv->msg; + iunlock(pv); + + iprint("%.*s", sizeof pv->msg, msg); +} + +static void +ucpoollock(Pool* p) +{ + Private *pv; + + pv = p->private; + ilock(pv); + pv->pc = getcallerpc(&p); + pv->cur = pv->msg; +} + +static void* +ucarena(usize size) +{ + void *uv, *v; + + assert(size == 1*MiB); + + mainmem->maxsize += 1*MiB; + if((v = mallocalign(1*MiB, 1*MiB, 0, 0)) == nil || + (uv = mmuuncache(v, 1*MiB)) == nil){ + free(v); + mainmem->maxsize -= 1*MiB; + return nil; + } + return uv; +} + +static Pool ucpool = { + .name = "Uncached", + .maxsize = 4*MiB, + .minarena = 1*MiB-32, + .quantum = 32, + .alloc = ucarena, + .merge = nil, + .flags = /*POOL_TOLERANCE|POOL_ANTAGONISM|POOL_PARANOIA|*/0, + + .lock = ucpoollock, + .unlock = ucpoolunlock, + .print = ucpoolprint, + .panic = ucpoolpanic, + + .private = &ucprivate, +}; + +void +ucfree(void* v) +{ + if(v == nil) + return; + poolfree(&ucpool, v); +} + +void* +ucalloc(usize size) +{ + assert(size < ucpool.minarena-128); + + return poolallocalign(&ucpool, size, 32, 0, 0); +} + +void* +ucallocalign(usize size, int align, int span) +{ + assert(size < ucpool.minarena-128); + + return poolallocalign(&ucpool, size, align, 0, span); +} diff --git a/sys/src/9/omap/ucallocb.c b/sys/src/9/omap/ucallocb.c new file mode 100755 index 000000000..954c5d96a --- /dev/null +++ b/sys/src/9/omap/ucallocb.c @@ -0,0 +1,152 @@ +/* + * allocate Blocks from uncached memory + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Hdrspc = 64, /* leave room for high-level headers */ + Bdead = 0x51494F42, /* "QIOB" */ +}; + +struct +{ + Lock; + ulong bytes; +} ucialloc; + +static Block* +_ucallocb(int size) +{ + Block *b; + ulong addr; + + if((b = ucalloc(sizeof(Block)+size+Hdrspc)) == nil) + return nil; + + b->next = nil; + b->list = nil; + b->free = 0; + b->flag = 0; + b->ref = 0; + _xinc(&b->ref); + + /* align start of data portion by rounding up */ + addr = (ulong)b; + addr = ROUND(addr + sizeof(Block), BLOCKALIGN); + b->base = (uchar*)addr; + + /* align end of data portion by rounding down */ + b->lim = ((uchar*)b) + msize(b); + addr = (ulong)(b->lim); + addr = addr & ~(BLOCKALIGN-1); + b->lim = (uchar*)addr; + + /* leave sluff at beginning for added headers */ + b->rp = b->lim - ROUND(size, BLOCKALIGN); + if(b->rp < b->base) + panic("_ucallocb"); + b->wp = b->rp; + + return b; +} + +Block* +ucallocb(int size) +{ + Block *b; + + /* + * Check in a process and wait until successful. + * Can still error out of here, though. + */ + if(up == nil) + panic("ucallocb without up: %#p", getcallerpc(&size)); + if((b = _ucallocb(size)) == nil) + panic("ucallocb: no memory for %d bytes", size); + setmalloctag(b, getcallerpc(&size)); + + return b; +} + +Block* +uciallocb(int size) +{ + Block *b; + static int m1, m2, mp; + + if(0 && ucialloc.bytes > conf.ialloc){ + if((m1++%10000)==0){ + if(mp++ > 1000){ + active.exiting = 1; + exit(0); + } + iprint("uciallocb: limited %lud/%lud\n", + ucialloc.bytes, conf.ialloc); + } + return nil; + } + + if((b = _ucallocb(size)) == nil){ + if(0 && (m2++%10000)==0){ + if(mp++ > 1000){ + active.exiting = 1; + exit(0); + } + iprint("uciallocb: no memory %lud/%lud\n", + ucialloc.bytes, conf.ialloc); + } + return nil; + } + setmalloctag(b, getcallerpc(&size)); + b->flag = BINTR; + + ilock(&ucialloc); + ucialloc.bytes += b->lim - b->base; + iunlock(&ucialloc); + + return b; +} + +void +ucfreeb(Block *b) +{ + void *dead = (void*)Bdead; + long ref; + + if(b == nil || (ref = _xdec(&b->ref)) > 0) + return; + + if(ref < 0){ + dumpstack(); + panic("ucfreeb: ref %ld; caller pc %#p", ref, getcallerpc(&b)); + } + + /* + * drivers which perform non cache coherent DMA manage their own buffer + * pool of uncached buffers and provide their own free routine. + */ + if(b->free) { + b->free(b); + return; + } + if(b->flag & BINTR) { + ilock(&ucialloc); + ucialloc.bytes -= b->lim - b->base; + iunlock(&ucialloc); + } + + /* poison the block in case someone is still holding onto it */ + b->next = dead; + b->rp = dead; + b->wp = dead; + b->lim = dead; + b->base = dead; + + ucfree(b); +} diff --git a/sys/src/9/omap/uncached.h b/sys/src/9/omap/uncached.h new file mode 100755 index 000000000..9fffd31fc --- /dev/null +++ b/sys/src/9/omap/uncached.h @@ -0,0 +1,36 @@ +/* + * running the l2 cache as write-back and using cached memory for + * usb data structures yields spurious errors such as + * + * qhintr: td 0x60ee3d80 csw 0x8824a error 0x48 transaction error + * + * from usbehci. so, at least for now, we will use uncached memory until + * we sort out the write-back problems. + */ +#define free ucfree +#define malloc myucalloc +#define mallocz ucallocz +#define smalloc myucalloc +#define xspanalloc ucallocalign + +#define allocb ucallocb +#define iallocb uciallocb +#define freeb ucfreeb + +static void * +ucallocz(uint n, int) +{ + char *p = ucalloc(n); + + if (p) + memset(p, 0, n); + else + panic("ucalloc: out of memory"); + return p; +} + +static void * +myucalloc(uint n) +{ + return ucallocz(n, 1); +} diff --git a/sys/src/9/omap/usbehci.h b/sys/src/9/omap/usbehci.h new file mode 100755 index 000000000..279b30a09 --- /dev/null +++ b/sys/src/9/omap/usbehci.h @@ -0,0 +1,243 @@ +/* override default macros from ../port/usb.h */ +#undef dprint +#undef ddprint +#undef deprint +#undef ddeprint +#define dprint if(ehcidebug)print +#define ddprint if(ehcidebug>1)print +#define deprint if(ehcidebug || ep->debug)print +#define ddeprint if(ehcidebug>1 || ep->debug>1)print + +typedef struct Ctlr Ctlr; +typedef struct Ecapio Ecapio; +typedef struct Eopio Eopio; +typedef struct Edbgio Edbgio; +typedef struct Isoio Isoio; +typedef struct Poll Poll; +typedef struct Qh Qh; +typedef struct Qtree Qtree; + +#pragma incomplete Ctlr; +#pragma incomplete Ecapio; +#pragma incomplete Eopio; +#pragma incomplete Edbgio; +#pragma incomplete Isoio; +#pragma incomplete Poll; +#pragma incomplete Qh; +#pragma incomplete Qtree; + + +/* + * EHCI interface registers and bits + */ +enum +{ + Cnports = 0xF, /* nport bits in Ecapio parms. */ + Cdbgportshift = 20, /* debug port in Ecapio parms. */ + Cdbgportmask = 0xF, + C64 = 1, /* 64-bits, in Ecapio capparms. */ + Ceecpshift = 8, /* extended capabilities ptr. in */ + Ceecpmask = 8, /* the Ecapio capparms reg. */ + Clegacy = 1, /* legacy support cap. id */ + CLbiossem = 2, /* legacy cap. bios sem. */ + CLossem = 3, /* legacy cap. os sem */ + CLcontrol = 4, /* legacy support control & status */ + + /* typed links */ + Lterm = 1, + Litd = 0<<1, + Lqh = 1<<1, + Lsitd = 2<<1, + Lfstn = 3<<1, /* we don't use these */ + + /* Cmd reg. */ + Cstop = 0x00000, /* stop running */ + Crun = 0x00001, /* start operation */ + Chcreset = 0x00002, /* host controller reset */ + Cflsmask = 0x0000C, /* frame list size bits */ + Cfls1024 = 0x00000, /* frame list size 1024 */ + Cfls512 = 0x00004, /* frame list size 512 frames */ + Cfls256 = 0x00008, /* frame list size 256 frames */ + Cpse = 0x00010, /* periodic sched. enable */ + Case = 0x00020, /* async sched. enable */ + Ciasync = 0x00040, /* interrupt on async advance doorbell */ + Citc1 = 0x10000, /* interrupt threshold ctl. 1 µframe */ + Citc4 = 0x40000, /* same. 2 µframes */ + /* ... */ + Citc8 = 0x80000, /* same. 8 µframes (can go up to 64) */ + + /* Sts reg. */ + Sasyncss = 0x08000, /* aync schedule status */ + Speriodss = 0x04000, /* periodic schedule status */ + Srecl = 0x02000, /* reclamnation (empty async sched.) */ + Shalted = 0x01000, /* h.c. is halted */ + Sasync = 0x00020, /* interrupt on async advance */ + Sherr = 0x00010, /* host system error */ + Sfrroll = 0x00008, /* frame list roll over */ + Sportchg = 0x00004, /* port change detect */ + Serrintr = 0x00002, /* error interrupt */ + Sintr = 0x00001, /* interrupt */ + Sintrs = 0x0003F, /* interrupts status */ + + /* Intr reg. */ + Iusb = 0x01, /* intr. on usb */ + Ierr = 0x02, /* intr. on usb error */ + Iportchg = 0x04, /* intr. on port change */ + Ifrroll = 0x08, /* intr. on frlist roll over */ + Ihcerr = 0x10, /* intr. on host error */ + Iasync = 0x20, /* intr. on async advance enable */ + Iall = 0x3F, /* all interrupts */ + + /* Config reg. */ + Callmine = 1, /* route all ports to us */ + + /* Portsc reg. */ + Pspresent = 0x00000001, /* device present */ + Psstatuschg = 0x00000002, /* Pspresent changed */ + Psenable = 0x00000004, /* device enabled */ + Pschange = 0x00000008, /* Psenable changed */ + Psresume = 0x00000040, /* resume detected */ + Pssuspend = 0x00000080, /* port suspended */ + Psreset = 0x00000100, /* port reset */ + Pspower = 0x00001000, /* port power on */ + Psowner = 0x00002000, /* port owned by companion */ + Pslinemask = 0x00000C00, /* line status bits */ + Pslow = 0x00000400, /* low speed device */ + + /* Debug port csw reg. */ + Cowner = 0x40000000, /* port owned by ehci */ + Cenable = 0x10000000, /* debug port enabled */ + Cdone = 0x00010000, /* request is done */ + Cbusy = 0x00000400, /* port in use by a driver */ + Cerrmask= 0x00000380, /* error code bits */ + Chwerr = 0x00000100, /* hardware error */ + Cterr = 0x00000080, /* transaction error */ + Cfailed = 0x00000040, /* transaction did fail */ + Cgo = 0x00000020, /* execute the transaction */ + Cwrite = 0x00000010, /* request is a write */ + Clen = 0x0000000F, /* data len */ + + /* Debug port pid reg. */ + Prpidshift = 16, /* received pid */ + Prpidmask = 0xFF, + Pspidshift = 8, /* sent pid */ + Pspidmask = 0xFF, + Ptokshift = 0, /* token pid */ + Ptokmask = 0xFF, + + Ptoggle = 0x00008800, /* to update toggles */ + Ptogglemask = 0x0000FF00, + + /* Debug port addr reg. */ + Adevshift = 8, /* device address */ + Adevmask = 0x7F, + Aepshift = 0, /* endpoint number */ + Aepmask = 0xF, +}; + +/* + * Capability registers (hw) + */ +struct Ecapio +{ + ulong cap; /* 00 controller capability register */ + ulong parms; /* 04 structural parameters register */ + ulong capparms; /* 08 capability parameters */ + ulong portroute; /* 0c not on the CS5536 */ +}; + +/* + * Debug port registers (hw) + */ +struct Edbgio +{ + ulong csw; /* control and status */ + ulong pid; /* USB pid */ + uchar data[8]; /* data buffer */ + ulong addr; /* device and endpoint addresses */ +}; + +struct Poll +{ + Lock; + Rendez; + int must; + int does; +}; + +struct Ctlr +{ + Rendez; /* for waiting to async advance doorbell */ + Lock; /* for ilock. qh lists and basic ctlr I/O */ + QLock portlck; /* for port resets/enable... (and doorbell) */ + int active; /* in use or not */ + Ecapio* capio; /* Capability i/o regs */ + Eopio* opio; /* Operational i/o regs */ + + int nframes; /* 1024, 512, or 256 frames in the list */ + ulong* frames; /* periodic frame list (hw) */ + Qh* qhs; /* async Qh circular list for bulk/ctl */ + Qtree* tree; /* tree of Qhs for the periodic list */ + int ntree; /* number of dummy qhs in tree */ + Qh* intrqhs; /* list of (not dummy) qhs in tree */ + Isoio* iso; /* list of active Iso I/O */ + ulong load; + ulong isoload; + int nintr; /* number of interrupts attended */ + int ntdintr; /* number of intrs. with something to do */ + int nqhintr; /* number of async td intrs. */ + int nisointr; /* number of periodic td intrs. */ + int nreqs; + Poll poll; +}; + +/* + * OMAP3-specific stuff + */ + +enum { + /* hostconfig bits */ + P1ulpi_bypass = 1<<0, /* utmi if set; else ulpi */ +}; + +/* + * Operational registers (hw) + */ +struct Eopio +{ + ulong cmd; /* 00 command */ + ulong sts; /* 04 status */ + ulong intr; /* 08 interrupt enable */ + ulong frno; /* 0c frame index */ + ulong seg; /* 10 bits 63:32 of EHCI datastructs (unused) */ + ulong frbase; /* 14 frame list base addr, 4096-byte boundary */ + ulong link; /* 18 link for async list */ + uchar d2c[0x40-0x1c]; /* 1c dummy */ + ulong config; /* 40 1: all ports default-routed to this HC */ + ulong portsc[3]; /* 44 Port status and control, one per port */ + + /* defined for omap35 ehci at least */ + uchar _pad0[0x80 - 0x50]; + ulong insn[6]; /* implementation-specific */ +}; + +typedef struct Uhh Uhh; +struct Uhh { + ulong revision; /* ro */ + uchar _pad0[0x10-0x4]; + ulong sysconfig; + ulong sysstatus; /* ro */ + + uchar _pad1[0x40-0x18]; + ulong hostconfig; + ulong debug_csr; +}; + +extern Ecapio *ehcidebugcapio; +extern int ehcidebugport; + +extern int ehcidebug; + +void ehcilinkage(Hci *hp); +void ehcimeminit(Ctlr *ctlr); +void ehcirun(Ctlr *ctlr, int on); diff --git a/sys/src/9/omap/usbehciomap.c b/sys/src/9/omap/usbehciomap.c new file mode 100755 index 000000000..a1592bb54 --- /dev/null +++ b/sys/src/9/omap/usbehciomap.c @@ -0,0 +1,224 @@ +/* + * OMAP3-specific code for + * USB Enhanced Host Controller Interface (EHCI) driver + * High speed USB 2.0. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/usb.h" +#include "usbehci.h" + +static Ctlr* ctlrs[Nhcis]; + +static void +ehcireset(Ctlr *ctlr) +{ + Eopio *opio; + int i; + + ilock(ctlr); + dprint("ehci %#p reset\n", ctlr->capio); + opio = ctlr->opio; + + /* + * Turn off legacy mode. Some controllers won't + * interrupt us as expected otherwise. + */ + ehcirun(ctlr, 0); + + /* clear high 32 bits of address signals if it's 64 bits capable. + * This is probably not needed but it does not hurt and others do it. + */ + if((ctlr->capio->capparms & C64) != 0){ + dprint("ehci: 64 bits\n"); + opio->seg = 0; + } + + if(ehcidebugcapio != ctlr->capio){ + opio->cmd |= Chcreset; /* controller reset */ + coherence(); + for(i = 0; i < 100; i++){ + if((opio->cmd & Chcreset) == 0) + break; + delay(1); + } + if(i == 100) + print("ehci %#p controller reset timed out\n", ctlr->capio); + } + + /* requesting more interrupts per µframe may miss interrupts */ + opio->cmd |= Citc8; /* 1 intr. per ms */ + coherence(); + switch(opio->cmd & Cflsmask){ + case Cfls1024: + ctlr->nframes = 1024; + break; + case Cfls512: + ctlr->nframes = 512; + break; + case Cfls256: + ctlr->nframes = 256; + break; + default: + panic("ehci: unknown fls %ld", opio->cmd & Cflsmask); + } + coherence(); + dprint("ehci: %d frames\n", ctlr->nframes); + iunlock(ctlr); +} + +static void +setdebug(Hci*, int d) +{ + ehcidebug = d; +} + +static void +shutdown(Hci *hp) +{ + int i; + Ctlr *ctlr; + Eopio *opio; + + ctlr = hp->aux; + ilock(ctlr); + opio = ctlr->opio; + opio->cmd |= Chcreset; /* controller reset */ + coherence(); + for(i = 0; i < 100; i++){ + if((opio->cmd & Chcreset) == 0) + break; + delay(1); + } + if(i >= 100) + print("ehci %#p controller reset timed out\n", ctlr->capio); + delay(100); + ehcirun(ctlr, 0); + opio->frbase = 0; + coherence(); + iunlock(ctlr); +} + +/* + * omap3-specific ehci code + */ + +enum { + /* opio->insn[5] bits */ + Control = 1<<31, /* set to start access, cleared when done */ + Write = 2<<22, + Read = 3<<22, + Portsh = 24, + Regaddrsh = 16, /* 0x2f means use extended reg addr */ + Eregaddrsh = 8, + + /* phy reg addresses */ + Funcctlreg = 4, + Ifcctlreg = 7, + + Phystppullupoff = 0x90, /* on is 0x10 */ + + Phyrstport2 = 147, /* gpio # */ + +}; + +static void +wrulpi(Eopio *opio, int port, int reg, uchar data) +{ + opio->insn[5] = Control | port << Portsh | Write | reg << Regaddrsh | + data; + coherence(); + /* + * this seems contrary to the skimpy documentation in the manual + * but inverting the test hangs forever. + */ + while (!(opio->insn[5] & Control)) + ; +} + +static int +reset(Hci *hp) +{ + Ctlr *ctlr; + Ecapio *capio; + Eopio *opio; + Uhh *uhh; + static int beenhere; + + if (beenhere) + return -1; + beenhere = 1; + + if(getconf("*nousbehci") != nil || probeaddr(PHYSEHCI) < 0) + return -1; + + ctlr = smalloc(sizeof(Ctlr)); + /* + * don't bother with vmap; i/o space is all mapped anyway, + * and a size less than 1MB will blow an assertion in mmukmap. + */ + ctlr->capio = capio = (Ecapio *)PHYSEHCI; + ctlr->opio = opio = (Eopio*)((uintptr)capio + (capio->cap & 0xff)); + + hp->aux = ctlr; + hp->port = (uintptr)ctlr->capio; + hp->irq = 77; + hp->nports = capio->parms & Cnports; + + ddprint("echi: %s, ncc %lud npcc %lud\n", + capio->parms & 0x10000 ? "leds" : "no leds", + (capio->parms >> 12) & 0xf, (capio->parms >> 8) & 0xf); + ddprint("ehci: routing %s, %sport power ctl, %d ports\n", + capio->parms & 0x40 ? "explicit" : "automatic", + capio->parms & 0x10 ? "" : "no ", hp->nports); + + ehcireset(ctlr); + ehcimeminit(ctlr); + + /* omap35-specific set up */ + /* bit 5 `must be set to 1 for proper behavior', spruf98d §23.2.6.7.17 */ + opio->insn[4] |= 1<<5; + coherence(); + + /* insn[5] is for both utmi and ulpi, depending on hostconfig mode */ + uhh = (Uhh *)PHYSUHH; + if (uhh->hostconfig & P1ulpi_bypass) { /* utmi port 1 active */ + /* not doing this */ + iprint("usbehci: bypassing ulpi on port 1!\n"); + opio->insn[5] &= ~(MASK(4) << 13); + opio->insn[5] |= 1 << 13; /* select port 1 */ + coherence(); + } else { /* ulpi port 1 active */ + /* TODO may need to reset gpio port2 here */ + + /* disable integrated stp pull-up resistor */ + wrulpi(opio, 1, Ifcctlreg, Phystppullupoff); + + /* force phy to `high-speed' */ + wrulpi(opio, 1, Funcctlreg, 0x40); + } + + /* + * Linkage to the generic HCI driver. + */ + ehcilinkage(hp); + hp->shutdown = shutdown; + hp->debug = setdebug; + + intrenable(78, hp->interrupt, hp, UNKNOWN, "usbtll"); + intrenable(92, hp->interrupt, hp, UNKNOWN, "usb otg"); + intrenable(93, hp->interrupt, hp, UNKNOWN, "usb otg dma"); + return 0; +} + +void +usbehcilink(void) +{ + addhcitype("ehci", reset); +} diff --git a/sys/src/9/omap/words b/sys/src/9/omap/words new file mode 100755 index 000000000..d55b335da --- /dev/null +++ b/sys/src/9/omap/words @@ -0,0 +1,202 @@ +beagleboard rev c3: +cortex-a8 cpu: arm v7-a arch. rev 3, 500MHz, dual-issue +OMAP3530-GP rev 2, CPU-OPP2 L3-165MHz +OMAP3 Beagle board + LPDDR/NAND +DRAM: 256 MB +NAND: 256 MiB +Board revision C +Serial #784200230000000004013f790401d018 + +igepv2 board: +cortex-a8 cpu: arm v7-a arch. rev 3, 720MHz, dual-issue +OMAP3530-GP ES3.1, CPU-OPP2 L3-165MHz +IGEP v2.x rev. B + LPDDR/ONENAND +DRAM: 512 MB +Muxed OneNAND(DDP) 512MB 1.8V 16-bit (0x58) +OneNAND version = 0x0031 +Chip support all block unlock +Chip has 2 plane +Scanning device for bad blocks +Bad eraseblock 3134 at 0x187c0000 +Bad eraseblock 3135 at 0x187e0000 +OneNAND: 512 MB + +omap3530 SoC +CORE_CLK runs at 26MHz +see spruf98d from ti.com (/public/doc/ti/omap35x.ref.spruf98d.pdf) + +separate i & d tlbs, each 32 entries + can invalidate i, d or both tlbs by { all, mva, or asid match } + +i & d L1 caches, 16K each, 4 ways, 64 sets, 64-byte lines + i is VIPT, d is PIPT + no `test and clean D & U all' operations + no prefetching, no cache maintenance + can invalidate i, d or both cache but not D & U all + can invalidate entire i-cache only + can clean or invalidate by set and way data/unified cache +unified L2 PIPT cache, 256K, 8 ways, 512 sets, 64-byte lines +no hardware cache coherence + +l3 interconnect firewalls are all off at boot time, except for a bit of + secure ram +sram at 0x40200000 size 1MB +l4 interconnect firewalls seem to be sane at boot time + +___ +The state of the Beagleboard/IGEPv2 (TI OMAP35 SoC, Cortex-A8) port. + +Plan 9 runs on the IGEPv2 and Gumstix Overo boards. + +On the Beagleboard, Plan 9 is not yet usable but it gets as far as +trying to access the USB ethernet (since the Beagleboard has no +built-in ethernet and must use USB ethernet). + +IGEP & Gumstix Ethernet + +The smsc9221 ethernet consumes a lot of system time. The design +decision to use fifos rather than buffer rings and to not incorporate +dma into the ethernet controller is probably responsible. With only a +single core, running the 9221 consumes a lot of the available CPU +time. It's probably worth trying to use the system dma controller again. + +USB + +The ohci and ehci controllers are seen, but no devices yet. + +There are four USB errata that need to be looked into for the igepv2 +(silicon 3.1) at least. From the omap3530 errata (rev e): + +- 3.1.1.130 only one usb dma channel (rx or tx) can be active + at one time: use interrupt mode instead +- 3.1.1.144 otg soft reset doesn't work right +- 3.1.1.183 ohci and ehci controllers cannot work concurrently +- §3.1.3 usb limitations: all ports must be configured to identical speeds + (high vs full/low) + +Flash + +access to nand flash would be handy for nvram and paqfs or sacfs file +systems. + +In the flash, x-loader occupies up to 0x20000, then u-boot from +0x80000 to 0x1e0000, and there's a linux kernel after that (if you +care). The beagle's flash chip is a micron pop 2Gb nand +mt29f2g16abdhc-et (physical marking jw256), and the igep's is a +samsung onenand. + +VFPv3 Floating Point + +The Cortex-A8 has VFPv3 floating point, which uses different opcodes +than 5c/5l currently generate. New 5c or 5l is in the works. + +Video + +The display subsystem for omap3 (dss) is divided into 3 parts, called lcd, +video and dsi (ignoring the various accelerators). The system only +supports the lcd via dvi interface so far because it's the only one we +have been able to test. 1280x1024x16 is the default resolution, this +might be changed. Writing to /dev/dssctl (e.g., echo 1024x768x16 +>/dev/dssctl) changes the resolution. Currently the system does not +use the rfbi since it seems like an unnecessary optimisation at this +point. Per Odlund wrote the first draft of the video driver for a +Google Summer of Code project. + +Stray Interrupts + +IRQs 56 and 57 are I2C. 83, 86 and 94 are MMC. + +___ + +The code is fairly heavy-handed with the use of barrier instructions +(BARRIERS in assembler, coherence in C), partly in reaction to bad +experience doing Power PC ports, but also just as precautions against +modern processors, which may feel free to execute instructions out of +order or some time later, store to memory out of order or some time +later, otherwise break the model of traditional sequential processors, +or any combination of the above. +___ + +There are a few rough edges: + +- the clock.c scheduling rate (HZ) is quite approximate. The OMAP +timers are complex, but one could eventually do better (or just let +timesync compensate). + +- User processes are limited to 512MB virtual (mainly by the IGEPv2 Ethernet +being at 0x2c000000), which isn't a problem since Beagleboards only +have 256MB of dram and IGEPv2s have 512MB, and we don't want to swap. + +- might use ucalloc.c to allocate uncached scratch space for generated code +in coproc.c. + +- the C implementation of cache primitives failed with mmu off; still true? + +- unlock, setup: protect module register target APE (PM_RT) per spruf98c §1.6.7 + +- setup mpp (multi-purpose pins)? + +___ + memory map (mostly from omap35x ref) +hex addr size what +---- +0 16MB physical address of flash registers, buffers +20000000 16MB virtual address of flash registers, buffers +2c000000 ? smc 9221 ethernet +38000000 16MB 256MB (beagle) or 512MB (igep) nand flash mapped here + +40000000 112K boot rom, top of user space +40200000 64K sram + +48000000 16MB L4 core +48002000 8K system control (scm) +48004000 16K clock manager +48040000 8K L4-core config +48050000 4K graphics +48062000 4K usb tll +48064000 1K usb uhh_config +48064400 1K ohci +48064800 1K ehci +4806a000 8K 8250 uart0 +4806c000 8K 8250 uart1 +48086000 4K gptimer10 +48088000 4K gptimer11 +4809c000 8K mmc/sd goo +480ab000 8K hs usb otg +480ad000 8K mmc/sd goo +480b4000 8K mmc/sd goo +480c7000 device intr controller +48200000 2K intr ctlr (intc) + +48300000 256K L4-wakeup +48304000 4K gptimer12 +48318000 8K gptimer1 + +49000000 1MB L4 peripherals +49020000 8K 8250 uart2 (with exposed connector for console) +49032000 4K gptimer2 +49034000 4K gptimer3 +⋯ +49040000 4K gptimer9 +49050000 8K gpio2 +⋯ +49058000 8K gpio6 + +50000000 64K graphics accelerator + +68000000 1K L3 config (rt) +68004000 1K L3 hs usb host +68004400 1K L3 hs usb otg +68005400 1K L3 graphics +68006800 1K L4-core config +68010000 L3 protection mechanism + +6e000000 ? gpmc + +80000000 256MB dram on beagle + 512MB dram on igep + +c0000000 1GB kernel virtual space, mapped to 80000000 + +apparently the vector address (0 or 0xffff0000) is virtual, +so we're expected to map it to ram. -- cgit v1.2.3