summaryrefslogtreecommitdiff
path: root/os/cerf405
diff options
context:
space:
mode:
Diffstat (limited to 'os/cerf405')
-rw-r--r--os/cerf405/NOTICE3
-rw-r--r--os/cerf405/README45
-rw-r--r--os/cerf405/cerf139
-rw-r--r--os/cerf405/clock.c159
-rw-r--r--os/cerf405/compile.c34
-rw-r--r--os/cerf405/dat.h159
-rw-r--r--os/cerf405/devboot.c132
-rw-r--r--os/cerf405/devether.c617
-rw-r--r--os/cerf405/devrtc.c369
-rw-r--r--os/cerf405/devuart.c1064
-rw-r--r--os/cerf405/etheremac.c829
-rw-r--r--os/cerf405/etherif.h37
-rw-r--r--os/cerf405/fns.h147
-rw-r--r--os/cerf405/fpi.h61
-rw-r--r--os/cerf405/fpipower.c970
-rw-r--r--os/cerf405/gpio.c106
-rw-r--r--os/cerf405/iic.c605
-rw-r--r--os/cerf405/inb.s127
-rw-r--r--os/cerf405/io.h301
-rw-r--r--os/cerf405/l.s795
-rw-r--r--os/cerf405/main.c719
-rw-r--r--os/cerf405/mal.c334
-rw-r--r--os/cerf405/mem.h147
-rw-r--r--os/cerf405/mkfile104
-rw-r--r--os/cerf405/mmu.c122
-rw-r--r--os/cerf405/nand.c96
-rw-r--r--os/cerf405/nofp.s31
-rw-r--r--os/cerf405/pci.c981
-rw-r--r--os/cerf405/physmem.h22
-rw-r--r--os/cerf405/powerbreak.c123
-rw-r--r--os/cerf405/rmap.c106
-rw-r--r--os/cerf405/tlb.s30
-rw-r--r--os/cerf405/trap.c581
-rw-r--r--os/cerf405/uart.c139
-rw-r--r--os/cerf405/uart.h101
35 files changed, 10335 insertions, 0 deletions
diff --git a/os/cerf405/NOTICE b/os/cerf405/NOTICE
new file mode 100644
index 00000000..0a613c43
--- /dev/null
+++ b/os/cerf405/NOTICE
@@ -0,0 +1,3 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved.
+PowerPC support Copyright © 1995-1997,2002,2003 C H Forsyth (forsyth@terzarima.net). All rights reserved.
+405EP Inferno PowerPC port Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
diff --git a/os/cerf405/README b/os/cerf405/README
new file mode 100644
index 00000000..c526927b
--- /dev/null
+++ b/os/cerf405/README
@@ -0,0 +1,45 @@
+Cerfcube 405EP
+
+This is the basis of a port to a range of IBM PowerPC 405 devices.
+At some point all the common code will move to ../ppc, as is done
+for SA1110, MPC8xx, and others.
+
+Currently untested:
+ - both EMAC0 and EMAC1 are initialised, but EMAC1 hasn't been tested (i need to get a cable made);
+ the PHY for it is detected and initialised, so i think it should work
+ - pci has not been tested since i haven't yet got the mini-PCI card i need
+
+Will change:
+ - devuart.c/uart.h will be replaced by ../port/devuart.c and uart405.c eventually
+ - power control will be extended
+
+mk in this directory should produce a file `icerfhd', which is icerf parcelled as ppcboot/uboot expects it.
+put it where your tftp server can find it.
+
+on the machine to be used as file server, run inferno and start svc/net
+to serve Styx
+
+To boot Inferno:
+ 1. set up a dhcp/tftp server for the EMAC0 MAC address, referring to the icerfhd file as the bootfile.
+ 2. connect a b115200 l8 pn serial port to talk to the ppcboot/uboot bootstrap
+ 3. reset the 405, and interrupt the automatic boot to get to the bootstrap's prompt
+ 4. type
+ bootp
+ 5. if dhcp is set correctly, that should load the Inferno boot image into memory at 0x300000
+ 6. type
+ bootm
+ to boot that image.
+
+To put Inferno kernel image (icerfhd) on NAND flash:
+ - the boot area is 0 to 0x100000 on NAND (perhaps a bit more, but 0x100000 is currently fine)
+ 1. get to the bootstrap's prompt.
+ 2. nand erase 0 0x100000
+ 3. bootp # loads the image to 0x300000 by tftp
+ 4. nand write 0x300000 0 0x100000
+ 5. a subsequent {nand; bootm} (as for auto boot) should run that kernel.
+
+To initialise a file system on the NAND flash:
+ 1. set logfsformat=yes in the environment
+ 2. boot inferno
+ 3. choose remote file system or kernel file system when offered
+ 4. the empty flash file system should be in /n/local
diff --git a/os/cerf405/cerf b/os/cerf405/cerf
new file mode 100644
index 00000000..64265586
--- /dev/null
+++ b/os/cerf405/cerf
@@ -0,0 +1,139 @@
+# Intrinsyc Cerf Cube 405EP
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ rtc iic
+ srv
+ dup
+ ssl
+ cap
+ sign
+
+ ip ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux
+ ether netif netaux
+ uart
+ flash
+
+ logfs
+ i2c iic
+ pci pci inb
+
+
+ip
+ il
+ tcp
+ udp
+# rudp
+# igmp
+ ipifc
+ icmp
+ icmp6
+ ipmux
+
+lib
+ interp
+ keyring
+ sec
+ mp
+ math
+ kern
+ logfs
+ nandfs
+
+link
+ etheremac ethermii
+ flashamd29f0x0
+ flashnand nand
+ ethermedium
+
+mod
+ math
+ sys
+ keyring
+
+port
+ alarm
+ alloc
+ allocb
+ chan
+ dev
+ dial
+ dis
+ discall
+ exception
+ exportfs
+ inferno
+ latin1
+ nocache
+ nodynld
+ parse
+ pgrp
+ print
+ proc
+ qio
+ qlock
+ random
+ sysfile
+ taslock
+ xalloc
+
+code
+ int cflag = 0;
+ int consoleprint = 1;
+ int panicreset = 0;
+ int main_pool_pcnt = 50;
+ int heap_pool_pcnt = 50;
+ int image_pool_pcnt = 0;
+ void screeninit(void){}
+
+init
+ cerf405
+
+root
+ /chan /
+ /dev /
+ /boot /
+ /env /
+ /fd /
+ /net /
+ /prog /
+ /root /
+ /nvfs /
+ /osinit.dis
+ /tmp /
+
+# files used by osinit.dis during bootstrap
+ /boot/n /
+ /boot/n/local /
+ /boot/n/remote /
+# authentication
+ /boot/nvfs/default /usr/inferno/keyring/default
+ /boot/dis/lib/auth.dis /dis/lib/auth.dis
+ /boot/dis/lib/ssl.dis /dis/lib/ssl.dis
+# dhcp
+ /boot/dis/lib/dhcpclient.dis /dis/lib/dhcpclient.dis
+ /boot/dis/lib/ip.dis /dis/lib/ip.dis
+
+# and other files used to poke round during development
+ /boot/dis/cat.dis /dis/cat.dis
+ /boot/dis/echo.dis /dis/echo.dis
+ /boot/dis/lib/arg.dis /dis/lib/arg.dis
+
+ /boot/dis/sh.dis /dis/sh.dis
+ /boot/dis/lib/bufio.dis /dis/lib/bufio.dis
+ /boot/dis/lib/filepat.dis /dis/lib/filepat.dis
+ /boot/dis/lib/readdir.dis /dis/lib/readdir.dis
+ /boot/dis/lib/string.dis /dis/lib/string.dis
+
+ /boot/dis/cd.dis /dis/cd.dis
+ /boot/dis/bind.dis /dis/bind.dis
+ /boot/dis/dd.dis /dis/dd.dis
+ /boot/dis/p.dis /dis/p.dis
+ /boot/dis/ls.dis /dis/ls.dis
+ /boot/dis/lib/daytime.dis /dis/lib/daytime.dis
+ /boot/dis/time.dis /dis/time.dis
+ /boot/dis/xd.dis /dis/xd.dis
diff --git a/os/cerf405/clock.c b/os/cerf405/clock.c
new file mode 100644
index 00000000..d830c18c
--- /dev/null
+++ b/os/cerf405/clock.c
@@ -0,0 +1,159 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+#include <isa.h>
+#include <interp.h>
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+ void (*clock)(void);
+ Clock0link* link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+ulong clkrelinq;
+void (*kproftick)(ulong); /* set by devkprof.c when active */
+void (*archclocktick)(void); /* set by arch*.c when desired */
+
+Timer*
+addclock0link(void (*clock)(void), int)
+{
+ Clock0link *lp;
+
+ if((lp = malloc(sizeof(Clock0link))) == 0){
+ print("addclock0link: too many links\n");
+ return nil;
+ }
+ ilock(&clock0lock);
+ lp->clock = clock;
+ lp->link = clock0link;
+ clock0link = lp;
+ iunlock(&clock0lock);
+ return nil;
+}
+
+void
+delay(int l)
+{
+ ulong i, j;
+
+ j = m->delayloop;
+ while(l-- > 0)
+ for(i=0; i < j; i++)
+ ;
+}
+
+void
+microdelay(int l)
+{
+ ulong i;
+
+ l *= m->delayloop;
+ l /= 1000;
+ if(l <= 0)
+ l = 1;
+ for(i = 0; i < l; i++)
+ ;
+}
+
+enum {
+ Timebase = 1, /* system clock cycles per time base cycle */
+
+ Wp17= 0<<30, /* watchdog period (2^x clocks) */
+ Wp21= 1<<30,
+ Wp25= 2<<30,
+ Wp29= 3<<30,
+ Wrnone= 0<<28, /* no watchdog reset */
+ Wrcore= 1<<28, /* core reset */
+ Wrchip= 2<<28, /* chip reset */
+ Wrsys= 3<<28, /* system reset */
+ Wie= 1<<27, /* watchdog interrupt enable */
+ Pie= 1<<26, /* enable PIT interrupt */
+ Fit9= 0<<24, /* fit period (2^x clocks) */
+ Fit13= 1<<24,
+ Fit17= 2<<24,
+ Fit21= 3<<24,
+ Fie= 1<<23, /* fit interrupt enable */
+ Are= 1<<22, /* auto reload enable */
+
+ /* dcr */
+ Boot= 0x0F1,
+ Epctl= 0x0F3,
+ Pllmr0= 0x0F0,
+ Pllmr1= 0x0F4,
+ Ucr= 0x0F5,
+};
+
+void
+clockinit(void)
+{
+ long x;
+
+ m->delayloop = m->cpuhz/1000; /* initial estimate */
+ do {
+ x = gettbl();
+ delay(10);
+ x = gettbl() - x;
+ } while(x < 0);
+
+ /*
+ * fix count
+ */
+ m->delayloop = ((vlong)m->delayloop*(10*(vlong)m->clockgen/1000))/(x*Timebase);
+ if((int)m->delayloop <= 0)
+ m->delayloop = 20000;
+
+ x = (m->clockgen/Timebase)/HZ;
+ putpit(x);
+iprint("pit value=%.8lux [%lud]\n", x, x);
+ puttsr(~0);
+ puttcr(Pie|Are);
+iprint("boot=%.8lux epctl=%.8lux pllmr0=%.8lux pllmr1=%.8lux ucr=%.8lux\n",
+ getdcr(Boot), getdcr(Epctl), getdcr(Pllmr0), getdcr(Pllmr1), getdcr(Ucr));
+}
+
+void
+clockintr(Ureg *ur)
+{
+ Clock0link *lp;
+
+ /* PIT was set to reload automatically */
+ puttsr(~0);
+ m->ticks++;
+
+ if(up)
+ up->pc = ur->pc;
+
+ if(archclocktick != nil)
+ archclocktick();
+ checkalarms();
+ if(m->machno == 0) {
+ if(kproftick != nil)
+ (*kproftick)(ur->pc);
+ lock(&clock0lock);
+ for(lp = clock0link; lp; lp = lp->link)
+ lp->clock();
+ unlock(&clock0lock);
+ }
+
+ if(up && up->state == Running){
+ if(cflag && up->type == Interp && tready(nil))
+ ur->cr |= 1; /* set flag in condition register for ../../interp/comp-power.c:/^schedcheck */
+ if(anyready())
+ sched();
+ }
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+ if(hz)
+ *hz = HZ;
+ return m->ticks;
+}
diff --git a/os/cerf405/compile.c b/os/cerf405/compile.c
new file mode 100644
index 00000000..da9976f2
--- /dev/null
+++ b/os/cerf405/compile.c
@@ -0,0 +1,34 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define MAXDCR 0x220
+
+#define DCRF(n) ((((n)>>5)&0x1F)|(((n)&0x1F)<<5))
+#define MTDCR(s,n) ((31<<26)|((s)<<21)|(DCRF(n)<<11)|(451<<1))
+#define MFDCR(n,t) ((31<<26)|((t)<<21)|(DCRF(n)<<11)|(323<<1))
+#define RETURN 0x4e800020
+ulong _getdcr[MAXDCR][2];
+ulong _putdcr[MAXDCR][2];
+
+void
+compiledcr(void)
+{
+ ulong *p;
+ int i;
+
+ for(i=0; i<MAXDCR; i++){
+ p = _getdcr[i];
+ p[0] = MFDCR(i, 3);
+ p[1] = RETURN;
+ p = _putdcr[i];
+ p[0] = MTDCR(3, i);
+ p[1] = RETURN;
+ }
+ dcflush(_getdcr, sizeof(_getdcr));
+ dcflush(_putdcr, sizeof(_putdcr));
+ /* no need to flush icache since they won't be there */
+}
diff --git a/os/cerf405/dat.h b/os/cerf405/dat.h
new file mode 100644
index 00000000..12af446e
--- /dev/null
+++ b/os/cerf405/dat.h
@@ -0,0 +1,159 @@
+typedef struct Conf Conf;
+typedef struct FPU FPU;
+typedef struct FPenv FPenv;
+typedef struct Irqctl Irqctl;
+typedef struct ISAConf ISAConf;
+typedef struct Label Label;
+typedef struct Lock Lock;
+typedef struct Mach Mach;
+typedef struct Map Map;
+typedef struct Pcidev Pcidev;
+typedef struct Power Power;
+typedef struct RMap RMap;
+typedef struct Ureg Ureg;
+
+typedef ulong Instr;
+
+#define MACHP(n) (n==0? &mach0 : *(Mach**)0)
+
+struct Lock
+{
+ ulong key;
+ ulong pc;
+ ulong sr;
+ int pri;
+};
+
+struct Label
+{
+ ulong sp;
+ ulong pc;
+};
+
+/*
+ * Proc.fpstate
+ */
+enum
+{
+ FPINIT,
+ FPACTIVE,
+ FPINACTIVE,
+};
+
+/*
+ * This structure must agree with FPsave and FPrestore asm routines
+ */
+struct FPenv
+{
+ union {
+ double fpscrd;
+ struct {
+ ulong pad;
+ ulong fpscr;
+ };
+ };
+ int fpistate; /* emulated fp */
+ ulong emreg[32][3]; /* emulated fp */
+};
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct FPU
+{
+ double fpreg[32];
+ FPenv env;
+};
+
+struct Conf
+{
+ ulong nmach; /* processors */
+ ulong nproc; /* processes */
+ ulong npage0; /* total physical pages of memory */
+ ulong npage1; /* total physical pages of memory */
+ ulong npage; /* total physical pages of memory */
+ ulong base0; /* base of bank 0 */
+ ulong base1; /* base of bank 1 */
+ ulong ialloc; /* max interrupt time allocation in bytes */
+};
+
+#include "../port/portdat.h"
+
+/*
+ * machine dependent definitions not used by ../port/dat.h
+ */
+
+struct Mach
+{
+ /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+ int machno; /* physical id of processor (unused) */
+ ulong splpc; /* pc of last caller to splhi (unused) */
+ int mmask; /* 1<<m->machno (unused) */
+
+ /* ordering from here on irrelevant */
+ ulong ticks; /* of the clock since boot time */
+ Proc *proc; /* current process on this processor */
+ Label sched; /* scheduler wakeup */
+ Lock alarmlock; /* access to alarm list */
+ void *alarm; /* alarms bound to this clock */
+ int nrdy;
+ int speed; /* general system clock in MHz */
+ long oscclk; /* oscillator frequency (MHz) */
+ long cpuhz; /* general system clock (cycles) */
+ long clockgen; /* clock generator frequency (cycles) */
+ long vcohz;
+ long pllhz;
+ long plbhz;
+ long opbhz;
+ long epbhz;
+ long pcihz;
+ int cputype;
+ ulong delayloop;
+
+ /* MUST BE LAST */
+ int stack[1];
+};
+extern Mach mach0;
+
+
+/*
+ * a parsed .ini line
+ */
+#define NISAOPT 8
+
+struct ISAConf {
+ char* type;
+ ulong port;
+ ulong irq;
+ ulong mem;
+ int dma;
+ ulong size;
+ ulong freq;
+ uchar bus;
+
+ int nopt;
+ char* opt[NISAOPT];
+};
+
+struct Map {
+ int size;
+ ulong addr;
+};
+
+struct RMap {
+ char* name;
+ Map* map;
+ Map* mapend;
+
+ Lock;
+};
+
+struct Power {
+ Dev* dev;
+ int (*powerdown)(Power*);
+ int (*powerup)(Power*);
+ int state;
+ void* arg;
+};
+
+extern register Mach *m;
+extern register Proc *up;
diff --git a/os/cerf405/devboot.c b/os/cerf405/devboot.c
new file mode 100644
index 00000000..126fe6e6
--- /dev/null
+++ b/os/cerf405/devboot.c
@@ -0,0 +1,132 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+enum{
+ Qdir,
+ Qboot,
+ Qmem,
+};
+
+Dirtab bootdir[]={
+ ".", {Qdir,0,QTDIR}, 0, 0555,
+ "boot", {Qboot}, 0, 0666,
+ "mem", {Qmem}, 0, 0666,
+};
+
+#define NBOOT (sizeof bootdir/sizeof(Dirtab))
+
+static void
+bootreset(void)
+{
+}
+
+static Chan*
+bootattach(char *spec)
+{
+ return devattach('B', spec);
+}
+
+static Walkqid*
+bootwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, bootdir, NBOOT, devgen);
+}
+
+static int
+bootstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, bootdir, NBOOT, devgen);
+}
+
+static Chan*
+bootopen(Chan *c, int omode)
+{
+ return devopen(c, omode, bootdir, NBOOT, devgen);
+}
+
+static void
+bootclose(Chan*)
+{
+}
+
+static long
+bootread(Chan *c, void *buf, long n, vlong off)
+{
+ ulong offset = off;
+
+ switch((ulong)c->qid.path){
+
+ case Qdir:
+ return devdirread(c, buf, n, bootdir, NBOOT, devgen);
+
+ case Qmem:
+ /* kernel memory */
+ if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){
+ if(offset+n > KZERO+conf.npage*BY2PG)
+ n = KZERO+conf.npage*BY2PG - offset;
+ memmove(buf, (char*)offset, n);
+ return n;
+ }
+ error(Ebadarg);
+ }
+
+ error(Egreg);
+ return 0; /* not reached */
+}
+
+static long
+bootwrite(Chan *c, void *buf, long n, vlong off)
+{
+ ulong offset = off;
+ ulong pc;
+ uchar *p;
+
+ switch((ulong)c->qid.path){
+ case Qmem:
+ /* kernel memory */
+ if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){
+ if(offset+n > KZERO+conf.npage*BY2PG)
+ n = KZERO+conf.npage*BY2PG - offset;
+ memmove((char*)offset, buf, n);
+ segflush((void*)offset, n);
+ return n;
+ }
+ error(Ebadarg);
+
+ case Qboot:
+ p = (uchar*)buf;
+ pc = (((((p[0]<<8)|p[1])<<8)|p[2])<<8)|p[3];
+ if(pc < KZERO || pc >= KZERO+conf.npage*BY2PG)
+ error(Ebadarg);
+ splhi();
+ segflush((void*)pc, 64*1024);
+ gotopc(pc);
+ }
+ error(Ebadarg);
+ return 0; /* not reached */
+}
+
+Dev bootdevtab = {
+ 'B',
+ "boot",
+
+ bootreset,
+ devinit,
+ devshutdown,
+ bootattach,
+ bootwalk,
+ bootstat,
+ bootopen,
+ devcreate,
+ bootclose,
+ bootread,
+ devbread,
+ bootwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
diff --git a/os/cerf405/devether.c b/os/cerf405/devether.c
new file mode 100644
index 00000000..eb4ed283
--- /dev/null
+++ b/os/cerf405/devether.c
@@ -0,0 +1,617 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+static Ether *etherxx[MaxEther];
+
+Chan*
+etherattach(char* spec)
+{
+ ulong ctlrno;
+ char *p;
+ Chan *chan;
+ Ether *ether;
+
+ ctlrno = 0;
+ if(spec && *spec){
+ ctlrno = strtoul(spec, &p, 0);
+ if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
+ error(Ebadarg);
+ }
+ if((ether = etherxx[ctlrno]) == 0)
+ error(Enodev);
+ rlock(ether);
+ if(waserror()){
+ runlock(ether);
+ nexterror();
+ }
+ chan = devattach('l', spec);
+ chan->dev = ctlrno;
+ if(ether->attach)
+ ether->attach(etherxx[ctlrno]);
+ poperror();
+ runlock(ether);
+ return chan;
+}
+
+static void
+ethershutdown(void)
+{
+ Ether *ether;
+ int i;
+
+ for(i=0; i<MaxEther; i++){
+ ether = etherxx[i];
+ if(ether != nil && ether->detach != nil)
+ ether->detach(ether);
+ }
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan *nchan, char **name, int nname)
+{
+ Walkqid *wq;
+ Ether *ether;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
+ poperror();
+ runlock(ether);
+ return wq;
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+ int s;
+ Ether *ether;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ s = netifstat(ether, chan, dp, n);
+ poperror();
+ runlock(ether);
+ return s;
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+ Chan *c;
+ Ether *ether;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ c = netifopen(ether, chan, omode);
+ poperror();
+ runlock(ether);
+ return c;
+}
+
+static void
+ethercreate(Chan*, char*, int, ulong)
+{
+}
+
+static void
+etherclose(Chan* chan)
+{
+ Ether *ether;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ netifclose(ether, chan);
+ poperror();
+ runlock(ether);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+ Ether *ether;
+ ulong offset = off;
+ long r;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ 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){
+ r = ether->ifstat(ether, buf, n, offset);
+ goto out;
+ }
+ if(NETTYPE(chan->qid.path) == Nstatqid)
+ ether->ifstat(ether, buf, 0, offset);
+ }
+ r = netifread(ether, chan, buf, n, offset);
+out:
+ poperror();
+ runlock(ether);
+ return r;
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+ Block *b;
+ Ether *ether;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ b = netifbread(ether, chan, n, offset);
+ poperror();
+ runlock(ether);
+ return b;
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+ Ether *ether;
+ int r;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ r = netifwstat(ether, chan, dp, n);
+ poperror();
+ runlock(ether);
+ return r;
+}
+
+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 = &ether->f[Ntypes];
+
+ multi = pkt->d[0] & 1;
+ /* check for valid multcast 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) && (f->type == type || f->type < 0))
+ if(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 onoff;
+ Cmdbuf *cb;
+ long l;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ if(NETTYPE(chan->qid.path) != Ndataqid) {
+ l = netifwrite(ether, chan, buf, n);
+ if(l >= 0)
+ goto out;
+ cb = parsecmd(buf, n);
+ if(strcmp(cb->f[0], "nonblocking") == 0){
+ if(cb->nf <= 1)
+ onoff = 1;
+ else
+ onoff = atoi(cb->f[1]);
+ qnoblock(ether->oq, onoff);
+ free(cb);
+ goto out;
+ }
+ free(cb);
+ if(ether->ctl!=nil){
+ l = ether->ctl(ether,buf,n);
+ goto out;
+ }
+ 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);
+ bp->wp += n;
+ poperror();
+
+ l = etheroq(ether, bp);
+out:
+ poperror();
+ runlock(ether);
+ return l;
+}
+
+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];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ if(n > ether->maxmtu){
+ freeb(bp);
+ error(Etoobig);
+ }
+ if(n < ether->minmtu){
+ freeb(bp);
+ error(Etoosmall);
+ }
+ n = etheroq(ether, bp);
+ poperror();
+ runlock(ether);
+ return n;
+}
+
+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;
+ ether->tbdf = BUSUNKNOWN;
+
+ if(archether(ctlrno, ether) <= 0)
+ 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, &ether->opt[i][3]) == -1)
+ memset(ether->ea, 0, Eaddrlen);
+ }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
+ cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
+ ether->fullduplex = 1;
+ 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)
+ intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
+
+ i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
+ ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
+ if(ether->mem)
+ i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+ if(ether->size)
+ i += sprint(buf+i, " size 0x%luX", ether->size);
+ i += sprint(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]);
+ sprint(buf+i, "\n");
+ print(buf);
+
+ if(ether->mbps == 100){
+ netifinit(ether, name, Ntypes, 256*1024);
+ if(ether->oq == 0)
+ ether->oq = qopen(256*1024, Qmsg, 0, 0);
+ }
+ else{
+ netifinit(ether, name, Ntypes, 64*1024);
+ if(ether->oq == 0)
+ ether->oq = qopen(64*1024, 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
+etherpower(int on)
+{
+ int i;
+ Ether *ether;
+
+ for(i = 0; i < MaxEther; i++){
+ if((ether = etherxx[i]) == nil || ether->power == nil)
+ continue;
+ if(on){
+ if(canrlock(ether))
+ continue;
+ if(ether->power != nil)
+ ether->power(ether, on);
+ wunlock(ether);
+ }else{
+ if(!canrlock(ether))
+ continue;
+ wlock(ether);
+ if(ether->power != nil)
+ ether->power(ether, on);
+ /* Keep locked until power goes back on */
+ }
+ }
+}
+
+#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;
+}
+
+Dev etherdevtab = {
+ 'l',
+ "ether",
+
+ etherreset,
+ devinit,
+ ethershutdown,
+ etherattach,
+ etherwalk,
+ etherstat,
+ etheropen,
+ ethercreate,
+ etherclose,
+ etherread,
+ etherbread,
+ etherwrite,
+ etherbwrite,
+ devremove,
+ etherwstat,
+ etherpower,
+};
diff --git a/os/cerf405/devrtc.c b/os/cerf405/devrtc.c
new file mode 100644
index 00000000..f743f1e0
--- /dev/null
+++ b/os/cerf405/devrtc.c
@@ -0,0 +1,369 @@
+/*
+ * DS1339 Timekeeper (on I2C)
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "io.h"
+
+typedef struct Rtc Rtc;
+typedef struct Rtcreg Rtcreg;
+
+struct Rtc
+{
+ int sec;
+ int min;
+ int hour;
+ int wday;
+ int mday;
+ int mon;
+ int year;
+};
+
+struct Rtcreg
+{
+ uchar sec;
+ uchar min;
+ uchar hour;
+ uchar wday; /* 1=Sun */
+ uchar mday; /* 00-31 */
+ uchar mon; /* 1-12 */
+ uchar year;
+};
+
+enum{
+ Qdir = 0,
+ Qrtc,
+
+ Rtclen= 7, /* bytes read and written to timekeeper */
+};
+
+static QLock rtclock; /* mutex on nvram operations */
+static I2Cdev rtdev;
+
+static Dirtab rtcdir[]={
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "rtc", {Qrtc, 0}, 0, 0664,
+};
+
+static ulong rtc2sec(Rtc*);
+static void sec2rtc(ulong, Rtc*);
+static void setrtc(Rtc*);
+
+static void
+rtcreset(void)
+{
+ rtdev.addr = 0x68;
+ rtdev.salen = 1;
+ i2csetup(1);
+}
+
+static Chan*
+rtcattach(char *spec)
+{
+ return devattach('r', spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
+}
+
+static int
+rtcstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+ omode = openmode(omode);
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ if(strcmp(up->env->user, eve)!=0 && omode!=OREAD)
+ error(Eperm);
+ break;
+ }
+ return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
+}
+
+static void
+rtcclose(Chan*)
+{
+}
+
+static long
+rtcread(Chan *c, void *buf, long n, vlong offset)
+{
+ ulong t, ot;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
+
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ qlock(&rtclock);
+ t = rtctime();
+ do{
+ ot = t;
+ t = rtctime(); /* make sure there's no skew */
+ }while(t != ot);
+ qunlock(&rtclock);
+ return readnum(offset, buf, n, t, 12);
+ }
+ error(Egreg);
+ return -1; /* never reached */
+}
+
+static long
+rtcwrite(Chan *c, void *buf, long n, vlong off)
+{
+ Rtc rtc;
+ ulong secs;
+ char *cp, sbuf[32];
+ ulong offset = off;
+
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ if(offset!=0 || n >= sizeof(sbuf)-1)
+ error(Ebadarg);
+ memmove(sbuf, buf, n);
+ sbuf[n] = '\0';
+ /*
+ * read the time
+ */
+ cp = sbuf;
+ while(*cp){
+ if(*cp>='0' && *cp<='9')
+ break;
+ cp++;
+ }
+ secs = strtoul(cp, 0, 0);
+ /*
+ * convert to bcd
+ */
+ sec2rtc(secs, &rtc);
+ /*
+ * write it
+ */
+ setrtc(&rtc);
+ return n;
+ }
+ error(Egreg);
+ return -1; /* never reached */
+}
+
+Dev rtcdevtab = {
+ 'r',
+ "rtc",
+
+ rtcreset,
+ devinit,
+ devshutdown,
+ rtcattach,
+ rtcwalk,
+ rtcstat,
+ rtcopen,
+ devcreate,
+ rtcclose,
+ rtcread,
+ devbread,
+ rtcwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+static int
+getbcd(int bcd)
+{
+ return (bcd&0x0f) + 10 * (bcd>>4);
+}
+
+static int
+putbcd(int val)
+{
+ return (val % 10) | (((val/10) % 10) << 4);
+}
+
+long
+rtctime(void)
+{
+ Rtc rtc;
+ Rtcreg d;
+ int h;
+
+ if(waserror()){
+ iprint("rtc: err %s\n", up->env->errstr);
+ return 0;
+ }
+ if(i2crecv(&rtdev, &d, Rtclen, 0) != Rtclen)
+ return 0;
+ poperror();
+ rtc.sec = getbcd(d.sec);
+ rtc.min = getbcd(d.min);
+ if(d.hour & (1<<6)){ /* 12 hour clock */
+ h = d.hour & 0x1F;
+ if(d.hour & (1<<5))
+ h += 0x12;
+ rtc.hour = getbcd(h);
+ }else
+ rtc.hour = getbcd(d.hour);
+ rtc.mday = getbcd(d.mday);
+ rtc.mon = getbcd(d.mon & 0x7f);
+ rtc.year = getbcd(d.year);
+ if(rtc.mon < 1 || rtc.mon > 12)
+ return 0;
+ if(d.mon & (1<<7))
+ rtc.year += 2000;
+ else
+ rtc.year += 1900;
+ return rtc2sec(&rtc);
+}
+
+static void
+setrtc(Rtc *rtc)
+{
+ Rtcreg d;
+
+ memset(&d, 0, sizeof(d));
+ d.year = putbcd(rtc->year % 100);
+ d.mon = putbcd(rtc->mon);
+ if(rtc->year >= 2000)
+ d.mon |= 1<<7;
+ d.wday = rtc->wday+1;
+ d.mday = putbcd(rtc->mday);
+ d.hour = putbcd(rtc->hour);
+ d.min = putbcd(rtc->min);
+ d.sec = putbcd(rtc->sec);
+ i2csend(&rtdev, &d, Rtclen, 0);
+}
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ * days per month plus days/year
+ */
+static int dmsize[] =
+{
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static int ldmsize[] =
+{
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * return the days/month for the given year
+ */
+static int *
+yrsize(int y)
+{
+
+ if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+/*
+ * compute seconds since Jan 1 1970
+ */
+static ulong
+rtc2sec(Rtc *rtc)
+{
+ ulong secs;
+ int i;
+ int *d2m;
+
+ secs = 0;
+
+ /*
+ * seconds per year
+ */
+ for(i = 1970; i < rtc->year; i++){
+ d2m = yrsize(i);
+ secs += d2m[0] * SEC2DAY;
+ }
+
+ /*
+ * seconds per month
+ */
+ d2m = yrsize(rtc->year);
+ for(i = 1; i < rtc->mon; i++)
+ secs += d2m[i] * SEC2DAY;
+
+ secs += (rtc->mday-1) * SEC2DAY;
+ secs += rtc->hour * SEC2HOUR;
+ secs += rtc->min * SEC2MIN;
+ secs += rtc->sec;
+
+ return secs;
+}
+
+/*
+ * compute rtc from seconds since Jan 1 1970
+ */
+static void
+sec2rtc(ulong secs, Rtc *rtc)
+{
+ int d;
+ long hms, day;
+ int *d2m;
+
+ /*
+ * break initial number into days
+ */
+ hms = secs % SEC2DAY;
+ day = secs / SEC2DAY;
+ if(hms < 0) {
+ hms += SEC2DAY;
+ day -= 1;
+ }
+
+ /*
+ * day is the day number.
+ * generate day of the week.
+ * The addend is 4 mod 7 (1/1/1970 was Thursday)
+ */
+
+ rtc->wday = (day + 7340036L) % 7;
+
+ /*
+ * generate hours:minutes:seconds
+ */
+ rtc->sec = hms % 60;
+ d = hms / 60;
+ rtc->min = d % 60;
+ d /= 60;
+ rtc->hour = d;
+
+ /*
+ * year number
+ */
+ if(day >= 0)
+ for(d = 1970; day >= *yrsize(d); d++)
+ day -= *yrsize(d);
+ else
+ for (d = 1970; day < 0; d--)
+ day += *yrsize(d-1);
+ rtc->year = d;
+
+ /*
+ * generate month
+ */
+ d2m = yrsize(rtc->year);
+ for(d = 1; day >= d2m[d]; d++)
+ day -= d2m[d];
+ rtc->mday = day + 1;
+ rtc->mon = d;
+}
diff --git a/os/cerf405/devuart.c b/os/cerf405/devuart.c
new file mode 100644
index 00000000..eba92cba
--- /dev/null
+++ b/os/cerf405/devuart.c
@@ -0,0 +1,1064 @@
+#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"
+
+/*
+ * Driver for the uart.
+ */
+enum
+{
+ /*
+ * register numbers
+ */
+ Data= 0, /* xmit/rcv buffer */
+ Iena= 1, /* interrupt enable */
+ Ircv= (1<<0), /* for char rcv'd */
+ Ixmt= (1<<1), /* for xmit buffer empty */
+ Irstat=(1<<2), /* for change in rcv'er status */
+ Imstat=(1<<3), /* for change in modem status */
+ Istat= 2, /* interrupt flag (read) */
+ Ipend= 1, /* interrupt pending (not) */
+ Fenabd=(3<<6), /* on if fifo's enabled */
+ Fifoctl=2, /* fifo control (write) */
+ Fena= (1<<0), /* enable xmit/rcv fifos */
+ Fdma= (1<<3), /* dma on */
+ Ftrig= (1<<6), /* trigger after 4 input characters */
+ Fclear=(3<<1), /* clear xmit & rcv fifos */
+ Format= 3, /* byte format */
+ Bits8= (3<<0), /* 8 bits/byte */
+ Stop2= (1<<2), /* 2 stop bits */
+ Pena= (1<<3), /* generate parity */
+ Peven= (1<<4), /* even parity */
+ Pforce=(1<<5), /* force parity */
+ Break= (1<<6), /* generate a break */
+ Dra= (1<<7), /* address the divisor */
+ Mctl= 4, /* modem control */
+ Dtr= (1<<0), /* data terminal ready */
+ Rts= (1<<1), /* request to send */
+ Ri= (1<<2), /* ring */
+ Inton= (1<<3), /* turn on interrupts */
+ Loop= (1<<4), /* loop back */
+ Lstat= 5, /* line status */
+ Inready=(1<<0), /* receive buffer full */
+ Oerror=(1<<1), /* receiver overrun */
+ Perror=(1<<2), /* receiver parity error */
+ Ferror=(1<<3), /* rcv framing error */
+ Berror=(1<<4), /* break alarm */
+ Outready=(1<<5), /* output buffer full */
+ Mstat= 6, /* modem status */
+ Ctsc= (1<<0), /* clear to send changed */
+ Dsrc= (1<<1), /* data set ready changed */
+ Rire= (1<<2), /* rising edge of ring indicator */
+ Dcdc= (1<<3), /* data carrier detect changed */
+ Cts= (1<<4), /* complement of clear to send line */
+ Dsr= (1<<5), /* complement of data set ready line */
+ Ringl= (1<<6), /* complement of ring indicator line */
+ Dcd= (1<<7), /* complement of data carrier detect line */
+ Scratch=7, /* scratchpad */
+ Dlsb= 0, /* divisor lsb */
+ Dmsb= 1, /* divisor msb */
+
+ CTLS= 023,
+ CTLQ= 021,
+
+ Stagesize= 1024,
+ Nuart= 2, /* max per machine */
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+ QLock;
+ int opens;
+
+ int enabled;
+ Uart *elist; /* next enabled interface */
+ char name[KNAMELEN];
+
+ uchar sticky[8]; /* sticky write register values */
+ void* regs;
+ ulong port;
+ ulong freq; /* clock frequency */
+ uchar mask; /* bits/char */
+ int dev;
+ int baud; /* baud rate */
+
+ uchar istat; /* last istat read */
+ int frame; /* framing errors */
+ int overrun; /* rcvr overruns */
+
+ /* buffers */
+ int (*putc)(Queue*, int);
+ Queue *iq;
+ Queue *oq;
+
+ Lock flock; /* fifo */
+ uchar fifoon; /* fifo's enabled */
+ uchar nofifo; /* earlier chip version with nofifo */
+
+ Lock rlock; /* receive */
+ uchar istage[Stagesize];
+ uchar *ip;
+ uchar *ie;
+
+ int haveinput;
+
+ Lock tlock; /* transmit */
+ uchar ostage[Stagesize];
+ uchar *op;
+ uchar *oe;
+
+ int modem; /* hardware flow control on */
+ int xonoff; /* software flow control on */
+ int blocked;
+ int cts, dsr, dcd; /* keep track of modem status */
+ int ctsbackoff;
+ int hup_dsr, hup_dcd; /* send hangup upstream? */
+ int dohup;
+
+ Rendez r;
+};
+
+static Uart *uart[Nuart];
+static int nuart;
+
+struct Uartalloc {
+ Lock;
+ Uart *elist; /* list of enabled interfaces */
+} uartalloc;
+
+static void uartintr(Uart*);
+
+/*
+ * pick up architecture specific routines and definitions
+ */
+#include "uart.h"
+
+/*
+ * set the baud rate by calculating and setting the baudrate
+ * generator constant. This will work with fairly non-standard
+ * baud rates.
+ */
+static void
+uartsetbaud(Uart *p, int rate)
+{
+ ulong brconst;
+
+ if(rate <= 0)
+ return;
+
+ p->freq = archuartclock(p->port, rate);
+ if(p->freq == 0)
+ return;
+
+ brconst = (p->freq+8*rate-1)/(16*rate);
+
+ uartwrreg(p, Format, Dra);
+ uartwr(p, Dmsb, (brconst>>8) & 0xff);
+ uartwr(p, Dlsb, brconst & 0xff);
+ uartwrreg(p, Format, 0);
+
+ p->baud = rate;
+}
+
+/*
+ * decide if we should hangup when dsr or dcd drops.
+ */
+static void
+uartdsrhup(Uart *p, int n)
+{
+ p->hup_dsr = n;
+}
+
+static void
+uartdcdhup(Uart *p, int n)
+{
+ p->hup_dcd = n;
+}
+
+static void
+uartparity(Uart *p, char type)
+{
+ switch(type){
+ case 'e':
+ p->sticky[Format] |= Pena|Peven;
+ break;
+ case 'o':
+ p->sticky[Format] &= ~Peven;
+ p->sticky[Format] |= Pena;
+ break;
+ default:
+ p->sticky[Format] &= ~(Pena|Peven);
+ break;
+ }
+ uartwrreg(p, Format, 0);
+}
+
+/*
+ * set bits/character, default 8
+ */
+void
+uartbits(Uart *p, int bits)
+{
+ if(bits < 5 || bits > 8)
+ error(Ebadarg);
+
+ p->sticky[Format] &= ~3;
+ p->sticky[Format] |= bits-5;
+
+ uartwrreg(p, Format, 0);
+}
+
+
+/*
+ * toggle DTR
+ */
+void
+uartdtr(Uart *p, int n)
+{
+ if(n)
+ p->sticky[Mctl] |= Dtr;
+ else
+ p->sticky[Mctl] &= ~Dtr;
+
+ uartwrreg(p, Mctl, 0);
+}
+
+/*
+ * toggle RTS
+ */
+void
+uartrts(Uart *p, int n)
+{
+ if(n)
+ p->sticky[Mctl] |= Rts;
+ else
+ p->sticky[Mctl] &= ~Rts;
+
+ uartwrreg(p, Mctl, 0);
+}
+
+/*
+ * send break
+ */
+static void
+uartbreak(Uart *p, int ms)
+{
+ if(ms == 0)
+ ms = 200;
+
+ uartwrreg(p, Format, Break);
+ tsleep(&up->sleep, return0, 0, ms);
+ uartwrreg(p, Format, 0);
+}
+
+static void
+uartfifoon(Uart *p)
+{
+ ulong i, x;
+
+ if(p->nofifo || uartrdreg(p, Istat) & Fenabd)
+ return;
+
+ x = splhi();
+
+ /* reset fifos */
+ p->sticky[Fifoctl] = 0;
+ uartwrreg(p, Fifoctl, Fclear);
+
+ /* empty buffer and interrupt conditions */
+ for(i = 0; i < 16; i++){
+ if(uartrdreg(p, Istat)){
+ /* nothing to do */
+ }
+ if(uartrdreg(p, Data)){
+ /* nothing to do */
+ }
+ }
+
+ /* turn on fifo */
+ p->fifoon = 1;
+ p->sticky[Fifoctl] = Fena|Ftrig;
+ uartwrreg(p, Fifoctl, 0);
+ p->istat = uartrdreg(p, Istat);
+ if((p->istat & Fenabd) == 0) {
+ /* didn't work, must be an earlier chip type */
+ p->nofifo = 1;
+ }
+
+ splx(x);
+}
+
+/*
+ * modem flow control on/off (rts/cts)
+ */
+static void
+uartmflow(Uart *p, int n)
+{
+ ilock(&p->tlock);
+ if(n){
+ p->sticky[Iena] |= Imstat;
+ uartwrreg(p, Iena, 0);
+ p->modem = 1;
+ p->cts = uartrdreg(p, Mstat) & Cts;
+ } else {
+ p->sticky[Iena] &= ~Imstat;
+ uartwrreg(p, Iena, 0);
+ p->modem = 0;
+ p->cts = 1;
+ }
+ iunlock(&p->tlock);
+
+// ilock(&p->flock);
+// if(1)
+// /* turn on fifo's */
+// uartfifoon(p);
+// else {
+// /* turn off fifo's */
+// p->fifoon = 0;
+// p->sticky[Fifoctl] = 0;
+// uartwrreg(p, Fifoctl, Fclear);
+// }
+// iunlock(&p->flock);
+}
+
+/*
+ * turn on a port's interrupts. set DTR and RTS
+ */
+static void
+uartenable(Uart *p)
+{
+ Uart **l;
+
+ if(p->enabled)
+ return;
+
+ uartportpower(p, 1);
+
+ p->hup_dsr = p->hup_dcd = 0;
+ p->cts = p->dsr = p->dcd = 0;
+
+ /*
+ * turn on interrupts
+ */
+ p->sticky[Iena] = Ircv | Ixmt | Irstat;
+ uartwrreg(p, Iena, 0);
+
+ /*
+ * turn on DTR and RTS
+ */
+ uartdtr(p, 1);
+ uartrts(p, 1);
+
+ uartfifoon(p);
+
+ /*
+ * assume we can send
+ */
+ ilock(&p->tlock);
+ p->cts = 1;
+ p->blocked = 0;
+ iunlock(&p->tlock);
+
+ /*
+ * set baud rate to the last used
+ */
+ uartsetbaud(p, p->baud);
+
+ lock(&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;
+ unlock(&uartalloc);
+}
+
+/*
+ * turn off a port's interrupts. reset DTR and RTS
+ */
+static void
+uartdisable(Uart *p)
+{
+ Uart **l;
+
+ /*
+ * turn off interrupts
+ */
+ p->sticky[Iena] = 0;
+ uartwrreg(p, Iena, 0);
+
+ /*
+ * revert to default settings
+ */
+ p->sticky[Format] = Bits8;
+ uartwrreg(p, Format, 0);
+
+ /*
+ * turn off DTR, RTS, hardware flow control & fifo's
+ */
+ uartdtr(p, 0);
+ uartrts(p, 0);
+ uartmflow(p, 0);
+ ilock(&p->tlock);
+ p->xonoff = p->blocked = 0;
+ iunlock(&p->tlock);
+
+ uartportpower(p, 0);
+
+ lock(&uartalloc);
+ for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+ if(*l == p){
+ *l = p->elist;
+ break;
+ }
+ }
+ p->enabled = 0;
+ unlock(&uartalloc);
+}
+
+/*
+ * put some bytes into the local queue to avoid calling
+ * qconsume for every character
+ */
+static int
+stageoutput(Uart *p)
+{
+ int n;
+
+ n = qconsume(p->oq, p->ostage, Stagesize);
+ if(n <= 0)
+ return 0;
+ p->op = p->ostage;
+ p->oe = p->ostage + n;
+ return n;
+}
+
+/*
+ * (re)start output
+ */
+static void
+uartkick0(Uart *p)
+{
+ int i;
+ if((p->modem && (p->cts == 0)) || p->blocked)
+ return;
+
+ /*
+ * 128 here is an arbitrary limit to make sure
+ * we don't stay in this loop too long. If the
+ * chips output queue is longer than 128, too
+ * bad -- presotto
+ */
+ for(i = 0; i < 128; i++){
+ if(!(uartrdreg(p, Lstat) & Outready))
+ break;
+ if(p->op >= p->oe && stageoutput(p) == 0)
+ break;
+ uartwr(p, Data, *p->op++);
+ }
+}
+
+static void
+uartkick(void *v)
+{
+ Uart *p;
+
+ p = v;
+ ilock(&p->tlock);
+ uartkick0(p);
+ iunlock(&p->tlock);
+}
+
+/*
+ * restart input if it's off
+ */
+static void
+uartflow(void *v)
+{
+ Uart *p;
+
+ p = v;
+ if(p->modem)
+ uartrts(p, 1);
+ ilock(&p->rlock);
+ p->haveinput = 1;
+ iunlock(&p->rlock);
+}
+
+/*
+ * default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts,
+ * transmit and receive enabled, interrupts disabled.
+ */
+static void
+uartsetup0(Uart *p)
+{
+ memset(p->sticky, 0, sizeof(p->sticky));
+ /*
+ * set rate to 9600 baud.
+ * 8 bits/character.
+ * 1 stop bit.
+ * interrupts enabled.
+ */
+ p->sticky[Format] = Bits8;
+ uartwrreg(p, Format, 0);
+ p->sticky[Mctl] |= Inton;
+ uartwrreg(p, Mctl, 0x0);
+
+ uartsetbaud(p, 9600);
+
+ p->iq = qopen(4*1024, 0, uartflow, p);
+ p->oq = qopen(4*1024, 0, uartkick, p);
+ if(p->iq == nil || p->oq == nil)
+ panic("uartsetup0");
+
+ p->ip = p->istage;
+ p->ie = &p->istage[Stagesize];
+ p->op = p->ostage;
+ p->oe = p->ostage;
+}
+
+/*
+ * called by uartinstall() to create a new duart
+ */
+void
+uartsetup(ulong port, void *regs, ulong freq, char *name)
+{
+ Uart *p;
+
+ if(nuart >= Nuart)
+ return;
+
+ p = xalloc(sizeof(Uart));
+ uart[nuart] = p;
+ strcpy(p->name, name);
+ p->dev = nuart;
+ nuart++;
+ p->port = port;
+ p->regs = regs;
+ p->freq = freq;
+ uartsetup0(p);
+}
+
+/*
+ * called by main() to configure a duart port as a console or a mouse
+ */
+void
+uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int))
+{
+ Uart *p = uart[port];
+ uartenable(p);
+ if(baud)
+ uartsetbaud(p, baud);
+ p->putc = putc;
+ if(in)
+ *in = p->iq;
+ if(out)
+ *out = p->oq;
+ p->opens++;
+}
+
+/*
+ * handle an interrupt to a single uart
+ */
+static void
+uartintr(Uart *p)
+{
+ uchar ch;
+ int s, l;
+
+ for (s = uartrdreg(p, Istat); !(s&Ipend); s = uartrdreg(p, Istat)) {
+ switch(s&0x3f){
+ case 4: /* received data available */
+ case 6: /* receiver line status (alarm or error) */
+ case 12: /* character timeout indication */
+ while ((l = uartrdreg(p, Lstat)) & Inready) {
+ if(l & Ferror)
+ p->frame++;
+ if(l & Oerror)
+ p->overrun++;
+ ch = uartrdreg(p, Data) & 0xff;
+ if (l & (Berror|Perror|Ferror)) {
+ /* ch came with break, parity or framing error - consume */
+ continue;
+ }
+ if (ch == CTLS || ch == CTLQ) {
+ ilock(&p->tlock);
+ if(p->xonoff){
+ if(ch == CTLS)
+ p->blocked = 1;
+ else
+ p->blocked = 0; /* clock gets output going again */
+ }
+ iunlock(&p->tlock);
+ }
+ if(p->putc)
+ p->putc(p->iq, ch);
+ else {
+ ilock(&p->rlock);
+ if(p->ip < p->ie)
+ *p->ip++ = ch;
+ else
+ p->overrun++;
+ p->haveinput = 1;
+ iunlock(&p->rlock);
+ }
+ }
+ break;
+
+ case 2: /* transmitter not full */
+ uartkick(p);
+ break;
+
+ case 0: /* modem status */
+ ch = uartrdreg(p, Mstat);
+ if(ch & Ctsc){
+ ilock(&p->tlock);
+ l = p->cts;
+ p->cts = ch & Cts;
+ if(l == 0 && p->cts)
+ p->ctsbackoff = 2; /* clock gets output going again */
+ iunlock(&p->tlock);
+ }
+ if (ch & Dsrc) {
+ l = ch & Dsr;
+ if(p->hup_dsr && p->dsr && !l){
+ ilock(&p->rlock);
+ p->dohup = 1;
+ iunlock(&p->rlock);
+ }
+ p->dsr = l;
+ }
+ if (ch & Dcdc) {
+ l = ch & Dcd;
+ if(p->hup_dcd && p->dcd && !l){
+ ilock(&p->rlock);
+ p->dohup = 1;
+ iunlock(&p->rlock);
+ }
+ p->dcd = l;
+ }
+ break;
+
+ default:
+ iprint("weird uart interrupt #%2.2ux\n", s);
+ break;
+ }
+ }
+ p->istat = s;
+}
+
+/*
+ * we save up input characters till clock time
+ *
+ * There's also a bit of code to get a stalled print going.
+ * It shouldn't happen, but it does. Obviously I don't
+ * understand something. Since it was there, I bundled a
+ * restart after flow control with it to give some hysteresis
+ * to the hardware flow control. This makes compressing
+ * modems happier but will probably bother something else.
+ * -- presotto
+ */
+void
+uartclock(void)
+{
+ int n;
+ Uart *p;
+
+ for(p = uartalloc.elist; p; p = p->elist){
+
+ /* this amortizes cost of qproduce to many chars */
+ if(p->haveinput){
+ ilock(&p->rlock);
+ if(p->haveinput){
+ n = p->ip - p->istage;
+ if(n > 0 && p->iq){
+ if(n > Stagesize)
+ panic("uartclock");
+ if(qproduce(p->iq, p->istage, n) < 0)
+ uartrts(p, 0);
+ else
+ p->ip = p->istage;
+ }
+ p->haveinput = 0;
+ }
+ iunlock(&p->rlock);
+ }
+ if(p->dohup){
+ ilock(&p->rlock);
+ if(p->dohup){
+ qhangup(p->iq, 0);
+ qhangup(p->oq, 0);
+ }
+ p->dohup = 0;
+ iunlock(&p->rlock);
+ }
+
+ /* this adds hysteresis to hardware flow control */
+ if(p->ctsbackoff){
+ ilock(&p->tlock);
+ if(p->ctsbackoff){
+ if(--(p->ctsbackoff) == 0)
+ uartkick0(p);
+ }
+ iunlock(&p->tlock);
+ }
+ }
+}
+
+Dirtab *uartdir;
+int ndir;
+
+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 < nuart; i++){
+ p = uart[i];
+ if(p && p->opens && p->iq)
+ uartdir[1+3*i].length = qlen(p->iq);
+ }
+}
+
+/*
+ * all uarts must be uartsetup() by this point or inside of uartinstall()
+ */
+static void
+uartreset(void)
+{
+ int i;
+ Dirtab *dp;
+ uartinstall(); /* architecture specific */
+
+ ndir = 1+3*nuart;
+ uartdir = xalloc(ndir * sizeof(Dirtab));
+ dp = uartdir;
+ strcpy(dp->name, ".");
+ mkqid(&dp->qid, 0, 0, QTDIR);
+ dp->length = 0;
+ dp->perm = DMDIR|0555;
+ dp++;
+ for(i = 0; i < nuart; i++){
+ /* 3 directory entries per port */
+ strcpy(dp->name, uart[i]->name);
+ dp->qid.path = NETQID(i, Ndataqid);
+ dp->perm = 0666;
+ dp++;
+ sprint(dp->name, "%sctl", uart[i]->name);
+ dp->qid.path = NETQID(i, Nctlqid);
+ dp->perm = 0666;
+ dp++;
+ sprint(dp->name, "%sstatus", uart[i]->name);
+ dp->qid.path = NETQID(i, Nstatqid);
+ dp->perm = 0444;
+ dp++;
+ }
+}
+
+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, ndir, 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, ndir, devgen);
+}
+
+static Chan*
+uartopen(Chan *c, int omode)
+{
+ Uart *p;
+
+ c = devopen(c, omode, uartdir, ndir, devgen);
+
+ switch(NETTYPE(c->qid.path)){
+ case Nctlqid:
+ case Ndataqid:
+ p = uart[NETID(c->qid.path)];
+ qlock(p);
+ if(p->opens++ == 0){
+ uartenable(p);
+ qreopen(p->iq);
+ qreopen(p->oq);
+ }
+ qunlock(p);
+ break;
+ }
+
+ return c;
+}
+
+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){
+ uartdisable(p);
+ qclose(p->iq);
+ qclose(p->oq);
+ p->ip = p->istage;
+ p->dcd = p->dsr = p->dohup = 0;
+ }
+ qunlock(p);
+ break;
+ }
+}
+
+static long
+uartstatus(Chan*, Uart *p, void *buf, long n, long offset)
+{
+ uchar mstat, fstat, istat, tstat;
+ char str[256];
+
+ str[0] = 0;
+ tstat = p->sticky[Mctl];
+ mstat = uartrdreg(p, Mstat);
+ istat = p->sticky[Iena];
+ fstat = p->sticky[Format];
+ snprint(str, sizeof str,
+ "b%d c%d d%d e%d l%d m%d p%c r%d s%d\n"
+ "%d %d %d%s%s%s%s%s\n",
+
+ p->baud,
+ p->hup_dcd,
+ (tstat & Dtr) != 0,
+ p->hup_dsr,
+ (fstat & Bits8) + 5,
+ (istat & Imstat) != 0,
+ (fstat & Pena) ? ((fstat & Peven) ? 'e' : 'o') : 'n',
+ (tstat & Rts) != 0,
+ (fstat & Stop2) ? 2 : 1,
+
+ p->dev,
+ p->frame,
+ p->overrun,
+ uartrdreg(p, Istat) & Fenabd ? " fifo" : "",
+ (mstat & Cts) ? " cts" : "",
+ (mstat & Dsr) ? " dsr" : "",
+ (mstat & Dcd) ? " dcd" : "",
+ (mstat & Ringl) ? " ring" : ""
+ );
+ return readstr(offset, buf, n, str);
+}
+
+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, ndir, 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 uartstatus(c, p, buf, n, offset);
+ }
+
+ return 0;
+}
+
+static void
+uartctl(Uart *p, char *cmd)
+{
+ int i, n;
+
+ /* let output drain for a while */
+ for(i = 0; i < 16 && qlen(p->oq); i++)
+ tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125);
+
+ if(strncmp(cmd, "break", 5) == 0){
+ uartbreak(p, 0);
+ return;
+ }
+
+
+ n = atoi(cmd+1);
+ switch(*cmd){
+ case 'B':
+ case 'b':
+ uartsetbaud(p, n);
+ break;
+ case 'C':
+ case 'c':
+ uartdcdhup(p, n);
+ break;
+ case 'D':
+ case 'd':
+ uartdtr(p, n);
+ break;
+ case 'E':
+ case 'e':
+ uartdsrhup(p, n);
+ break;
+ case 'f':
+ case 'F':
+ qflush(p->oq);
+ break;
+ case 'H':
+ case 'h':
+ qhangup(p->iq, 0);
+ qhangup(p->oq, 0);
+ break;
+ case 'L':
+ case 'l':
+ uartbits(p, n);
+ break;
+ case 'm':
+ case 'M':
+ uartmflow(p, n);
+ break;
+ case 'n':
+ case 'N':
+ qnoblock(p->oq, n);
+ break;
+ case 'P':
+ case 'p':
+ uartparity(p, *(cmd+1));
+ break;
+ case 'K':
+ case 'k':
+ uartbreak(p, n);
+ break;
+ case 'R':
+ case 'r':
+ uartrts(p, n);
+ break;
+ case 'Q':
+ case 'q':
+ qsetlimit(p->iq, n);
+ qsetlimit(p->oq, n);
+ break;
+ case 'W':
+ case 'w':
+ /* obsolete */
+ break;
+ case 'X':
+ case 'x':
+ ilock(&p->tlock);
+ p->xonoff = n;
+ iunlock(&p->tlock);
+ break;
+ }
+}
+
+static long
+uartwrite(Chan *c, void *buf, long n, vlong)
+{
+ Uart *p;
+ char cmd[32];
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ p = uart[NETID(c->qid.path)];
+
+ /*
+ * The fifo's turn themselves off sometimes.
+ * It must be something I don't understand. -- presotto
+ */
+ lock(&p->flock);
+ if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0)
+ uartfifoon(p);
+ unlock(&p->flock);
+
+ switch(NETTYPE(c->qid.path)){
+ case Ndataqid:
+ return qwrite(p->oq, buf, n);
+ case Nctlqid:
+ if(n >= sizeof(cmd))
+ n = sizeof(cmd)-1;
+ memmove(cmd, buf, n);
+ cmd[n] = 0;
+ uartctl(p, cmd);
+ return n;
+ }
+}
+
+static int
+uartwstat(Chan *c, uchar *dp, int n)
+{
+ Dir d;
+ Dirtab *dt;
+
+ if(!iseve())
+ error(Eperm);
+ if(c->qid.type & QTDIR)
+ 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){
+ d.mode &= 0666;
+ dt[0].perm = dt[1].perm = d.mode;
+ }
+ return n;
+}
+
+Dev uartdevtab = {
+ 't',
+ "uart",
+
+ uartreset,
+ devinit,
+ devshutdown,
+ uartattach,
+ uartwalk,
+ uartstat,
+ uartopen,
+ devcreate,
+ uartclose,
+ uartread,
+ devbread,
+ uartwrite,
+ devbwrite,
+ devremove,
+ uartwstat,
+};
diff --git a/os/cerf405/etheremac.c b/os/cerf405/etheremac.c
new file mode 100644
index 00000000..484c4205
--- /dev/null
+++ b/os/cerf405/etheremac.c
@@ -0,0 +1,829 @@
+/*
+ * ethernet
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "ethermii.h"
+#include "etherif.h"
+#include "ureg.h"
+
+/*
+ * TO DO:
+ * - test EMAC1
+ */
+
+#define DBG if(0)iprint
+#define MIIDBG if(0)iprint
+
+enum {
+ Nrdre = 64, /* receive descriptor ring entries */
+ Ntdre = 32, /* transmit descriptor ring entries */
+ Nrxchan = 2,
+ Ntxchan = 2, /* there are actually 4 but we only use 2 now */
+
+ Rbsize = ETHERMAXTU, /* ring buffer size */
+ Bufsize = (Rbsize+CACHELINESZ-1)&~(CACHELINESZ-1), /* aligned */
+};
+
+enum {
+ /* emac-specific Rx BD bits */
+ RxOverrun= 1<<9, /* not enough empty space in FIFO */
+ RxPause= 1<<8, /* control pause packet */
+ RxBad= 1<<7, /* packet error */
+ RxRunt= 1<<6,
+ RxShort= 1<<5,
+ RxAlign= 1<<4,
+ RxFCS= 1<<3,
+ RxLong= 1<<2,
+ RxRange= 1<<1, /* out of range error */
+ RxInRange= 1<<0, /* in range error */
+ RxError= (0x3FF & ~RxPause), /* error flags */
+
+ /* emac-specific Tx BD bits */
+ /* write access */
+ TxFCS= 1<<9, /* generate FCS */
+ TxPad= 1<<8, /* pad short frames */
+ TxInsSA= 1<<7, /* insert source address */
+ TxRepSA= 1<<6, /* replace source address */
+ TxInsVLAN= 1<<5, /* insert VLAN tag */
+ TxRepVLAN= 1<<4, /* replace VLAN tag */
+
+ /* read access (status) */
+ TxBadFCS= 1<<9,
+ TxBadPrev= 1<<8, /* bad previous packet in dependent mode */
+ TxLostCarrier= 1<<7,
+ TxEDef= 1<<6, /* excessive deferral */
+ TxECol= 1<<5, /* excessive collisions */
+ TxLateCol= 1<<4, /* late collision (half-duplex only) */
+ TxManyCol= 1<<3, /* more than 1 but less than 16 collisions */
+ TxCollision= 1<<2, /* single collision */
+ TxUnderrun= 1<<1, /* didn't fill FIFO in time */
+ TxSQE= 1<<0, /* signal quality test failed (10mbit half-duplex only) */
+ TxError= 0x3FF, /* error flags */
+};
+
+typedef struct Emac Emac;
+struct Emac {
+ ulong mr0; /* mode register 0 [see 19-48] */
+ ulong mr1; /* mode register 1 [Reset] */
+ ulong tmr0; /* transmit mode register 0 [see 19-28] */
+ ulong tmr1; /* transmit mode register 1 [see 19-28] */
+ ulong rmr; /* receive mode register [Reset] */
+ ulong isr; /* interrupt status register [Always] */
+ ulong iser; /* interrupt status enable register [Reset] */
+ ulong iahr; /* individual address high [Reset, R, T]*/
+ ulong ialr; /* individual address low [Reset, R, T] */
+ ulong vtpid; /* VLAN Tag Protocol Identifier [Reset, R, T] */
+ ulong vtci; /* VLAN Tag Control Information [Reset, R, T] */
+ ulong ptr; /* pause timer [Reset, T] */
+ ulong iaht[4]; /* individual address hash table [Reset, R] */
+ ulong gaht[4]; /* group address hash table [Reset, R] */
+ ulong lsah; /* last source address high */
+ ulong lsal; /* last source address low */
+ ulong ipgvr; /* inter-packet gap value [Reset, T] */
+ ulong stacr; /* STA control register [see 19-41] */
+ ulong trtr; /* transmit request threshold register [see 19-42] */
+ ulong rwmr; /* receive low/high water mark [Reset] */
+ ulong octx; /* bytes transmitted */
+ ulong ocrx; /* bytes received */
+};
+
+enum {
+ /* mode register 0 */
+ Mr0Rxi= 1<<31, /* receive MAC idle */
+ Mr0Txi= 1<<30, /* transmit MAC idle */
+ Mr0Srst= 1<<29, /* soft reset; soft reset in progress */
+ Mr0Txe= 1<<28, /* tx MAC enable */
+ Mr0Rxe= 1<<27, /* rx MAC enable */
+ Mr0Wke= 1<<26, /* enable wake-up packets */
+
+ /* mode register 1 */
+ Mr1Fde= 1<<31, /* full-duplex enable */
+ Mr1Ile= 1<<30, /* internal loop-back enable */
+ Mr1Vle= 1<<29, /* VLAN enable */
+ Mr1Eifc= 1<<28, /* enable integrated flow control */
+ Mr1App= 1<<27, /* allow pause packets */
+ Mr1Ist= 1<<24, /* ignore sqe test (all but half-duplex 10m/bit) */
+ Mr1Mf10= 0<<22, /* medium [MII] frequency is 10 mbps */
+ Mr1Mf100= 1<<22, /* medium frequency is 100 mbps */
+ Mr1Rfs512= 0<<20, /* RX FIFO size (512 bytes) */
+ Mr1Rfs1024= 1<<20,
+ Mr1Rfs2048= 2<<20,
+ Mr1Rfs4096= 3<<20,
+ Mr1Tfs1024= 1<<18, /* TX FIFO size (1024 bytes) */
+ Mr1Tfs2048= 2<<18,
+ Mr1Tr0sp= 0<<15, /* transmit request 0: single packet */
+ Mr1Tr0mp= 1<<15, /* multiple packets */
+ Mr1Tr0dm= 2<<15, /* dependent mode */
+ Mr1Tr1sp= 0<<13, /* transmit request 1: single packet */
+ Mr1Tr1mp= 1<<13, /* multiple packets */
+ Mr1Tr1dm= 2<<13, /* dependent mode */
+
+ /* transmit mode register 0 */
+ Tmr0Gnp0= 1<<31, /* get new packet channel 0 */
+ Tmr0Gnp1= 1<<30, /* get new packet channel 1 */
+ Tmr0Gnpd= 1<<29, /* get new packet dependent mode */
+ Tmr0Fc= 1<<28, /* first channel (dependent mode) */
+
+ /* transmit mode register 1 */
+ Tmr1Trl_s= 27, /* transmit low request (shift) */
+ Tmr1Tur_s= 16, /* transmit urgent request (shift) */
+
+ /* receive mode register */
+ RmrSp= 1<<31, /* strip pad/FCS bytes */
+ RmrSfcs= 1<<30, /* strip FCS */
+ RmrRrp= 1<<29, /* receive runt packets */
+ RmrRfp= 1<<28, /* receive packets with FCS error */
+ RmrRop= 1<<27, /* receive oversize packets */
+ RmrRpir= 1<<26, /* receive packets with in range error */
+ RmrPpp= 1<<25, /* propagate pause packet */
+ RmrPme= 1<<24, /* promiscuous mode enable */
+ RmrPmme= 1<<23, /* promiscuous mode multicast enable */
+ RmrIae= 1<<22, /* individual address enable */
+ RmrMiae= 1<<21, /* multiple individual address enable */
+ RmrBae= 1<<20, /* broadcast address enable */
+ RmrMae= 1<<19, /* multicast address enable */
+
+ /* interrupt status register */
+ IsrOvr= 1<<25, /* overrun error */
+ IsrPp= 1<<24, /* pause packet */
+ IsrBp= 1<<23, /* bad packet */
+ IsrRp= 1<<22, /* runt packet */
+ IsrSe= 1<<21, /* short event */
+ IsrAle= 1<<20, /* alignment error */
+ IsrBfcs= 1<<19, /* bad FCS */
+ IsrPtle= 1<<18, /* packet too long error */
+ IsrOre= 1<<17, /* out of range error */
+ IsrIre= 1<<16, /* in range error */
+ IsrDbdm= 1<<9, /* dead bit dependent mode */
+ IsrDb0= 1<<8, /* dead bit 0 */
+ IsrSe0= 1<<7, /* sqe 0 */
+ IsrTe0= 1<<6, /* tx error 0 */
+ IsrDb1= 1<<5, /* dead bit 1 */
+ IsrSe1= 1<<4, /* sqe 1 */
+ IsrTe1= 1<<3, /* tx error 1 */
+ IsrMos= 1<<1, /* MMA operation succeeded */
+ IsrMof= 1<<0, /* MMA operation failed */
+
+ /* STA control register */
+ StaOc= 1<<15, /* operation complete */
+ StaPhye= 1<<14, /* PHY error */
+ StaRead= 1<<12, /* STA read */
+ StaWrite= 2<<12, /* STA write */
+ StaOpb50= 0<<10, /* OPB frequency */
+ StaOpb66= 1<<10,
+ StaOpb83= 2<<10,
+ StaOpb100= 3<<10,
+
+ /* transmit request threshold */
+ TrtrTrt_s= 27, /* threshold (shift) -- and the value is (threshold/64)-1 */
+
+ /* receive low/high water mark register */
+ RwmrRlwm_s= 23, /* low water mark (shift) */
+ RwmrRhwm_s= 7, /* high water mark (shift) */
+};
+
+typedef struct {
+ Lock;
+ int port;
+ int init;
+ int active;
+ Emac *regs;
+ Emac *miiregs;
+ Mal* rx;
+ Mal* tx;
+
+ Mii *mii;
+
+ Ring;
+
+ ulong interrupts; /* statistics */
+ ulong deferred;
+ ulong heartbeat;
+ ulong latecoll;
+ ulong retrylim;
+ ulong underrun;
+ ulong overrun;
+ ulong carrierlost;
+ ulong retrycount;
+} Ctlr;
+
+static void dumpemac(Emac*);
+
+static void
+attach(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(ctlr);
+ if(!ctlr->active){
+ malrxenable(ctlr->rx);
+ maltxenable(ctlr->tx);
+ eieio();
+ ctlr->regs->mr0 = Mr0Txe | Mr0Rxe;
+ eieio();
+ ctlr->active = 1;
+ }
+ iunlock(ctlr);
+}
+
+static void
+closed(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ if(ctlr->active){
+ ilock(ctlr);
+iprint("ether closed\n");
+ ctlr->regs->mr0 &= ~(Mr0Txe | Mr0Rxe); /* reset enable bits */
+ /* TO DO: reset ring */
+ /* TO DO: could wait */
+ ctlr->active = 0;
+ iunlock(ctlr);
+ }
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+
+ ether = (Ether*)arg;
+ ctlr = ether->ctlr;
+
+ ilock(ctlr);
+ if(on || ether->nmaddr)
+ ctlr->regs->rmr |= RmrPme | RmrPmme;
+ else
+ ctlr->regs->rmr &= ~(RmrPme | RmrPmme);
+ iunlock(ctlr);
+}
+
+static void
+multicast(void* arg, uchar *addr, int on)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+
+ USED(addr, on); /* if on, could SetGroupAddress; if !on, it's hard */
+
+ ether = (Ether*)arg;
+ ctlr = ether->ctlr;
+
+ ilock(ctlr);
+ if(ether->prom || ether->nmaddr)
+ ctlr->regs->rmr |= RmrPmme;
+ else
+ ctlr->regs->rmr &= ~RmrPmme;
+ iunlock(ctlr);
+}
+
+static void
+txstart(Ether *ether)
+{
+ int len;
+ Ctlr *ctlr;
+ Block *b;
+ BD *dre;
+
+ ctlr = ether->ctlr;
+ while(ctlr->ntq < ctlr->ntdre-1){
+ b = qget(ether->oq);
+ if(b == 0)
+ break;
+
+ dre = &ctlr->tdr[ctlr->tdrh];
+ if(dre->status & BDReady)
+ panic("ether: txstart");
+
+ /*
+ * Give ownership of the descriptor to the chip, increment the
+ * software ring descriptor pointer and tell the chip to poll.
+ */
+ len = BLEN(b);
+ if(ctlr->txb[ctlr->tdrh] != nil)
+ panic("etheremac: txstart");
+ ctlr->txb[ctlr->tdrh] = b;
+ dre->addr = PADDR(b->rp);
+ dre->length = len;
+ dcflush(b->rp, len);
+ eieio();
+ dre->status = (dre->status & BDWrap) | BDReady|BDInt|BDLast|TxFCS|TxPad;
+ eieio();
+ ctlr->regs->tmr0 = Tmr0Gnp0; /* TO DO: several channels */
+ eieio();
+ ctlr->ntq++;
+ ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdre);
+ }
+}
+
+static void
+transmit(Ether* ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(ctlr);
+ txstart(ether);
+ iunlock(ctlr);
+}
+
+/*
+ * allocate receive buffer space on cache-line boundaries
+ */
+static Block*
+clallocb(void)
+{
+ Block *b;
+
+ b = iallocb(Bufsize+CACHELINESZ-1);
+ if(b == nil)
+ return b;
+ dcflush(b->base, BALLOC(b));
+ b->wp = b->rp = (uchar*)(((ulong)b->base + CACHELINESZ - 1) & ~(CACHELINESZ-1));
+ return b;
+}
+
+
+static void
+rxring(Ureg*, void *arg)
+{
+ Ether *ether;
+ ulong status;
+ Ctlr *ctlr;
+ BD *dre;
+ Block *b, *rb;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+ ctlr->interrupts++;
+
+ /*
+ * Receiver interrupt: run round the descriptor ring logging
+ * errors and passing valid receive data up to the higher levels
+ * until we encounter a descriptor still owned by the chip.
+ * We rely on the descriptor accesses being uncached.
+ */
+ dre = &ctlr->rdr[ctlr->rdrx];
+ while(((status = dre->status) & BDEmpty) == 0){
+ if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){
+ if(status & (RxShort|RxLong))
+ ether->buffs++;
+ if(status & (RxBad|RxAlign|RxRange|RxInRange))
+ ether->frames++;
+ if(status & RxFCS)
+ ether->crcs++;
+ if(status & RxOverrun)
+ ether->overflows++;
+ iprint("eth rx: %lux\n", status);
+ }else if((status & RxPause) == 0){
+ /*
+ * We have a packet. Read it in.
+ */
+ b = clallocb();
+ if(b != nil){
+ rb = ctlr->rxb[ctlr->rdrx];
+ rb->wp += dre->length;
+ ctlr->rxb[ctlr->rdrx] = b;
+ ctlr->rdr[ctlr->rdrx].addr = PADDR(b->wp);
+ etheriq(ether, rb, 1);
+ }else
+ ether->soverflows++;
+ }
+
+ /*
+ * Finished with this descriptor, reinitialise it,
+ * give it back to the chip, then on to the next...
+ */
+ dre->status = (status & BDWrap) | BDEmpty | BDInt;
+ eieio();
+
+ ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdre);
+ dre = &ctlr->rdr[ctlr->rdrx];
+ }
+}
+
+static void
+txring(Ureg*, void *arg)
+{
+ Ether *ether;
+ ulong status;
+ Ctlr *ctlr;
+ BD *dre;
+ Block *b;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+ ctlr->interrupts++;
+
+ /*
+ * Transmitter interrupt: handle anything queued for a free descriptor.
+ */
+ lock(ctlr);
+ while(ctlr->ntq){
+ dre = &ctlr->tdr[ctlr->tdri];
+ status = dre->status;
+ if(status & BDReady)
+ break;
+ if(status & TxEDef)
+ ctlr->deferred++;
+ if(status & TxLateCol)
+ ctlr->latecoll++;
+ if(status & TxECol)
+ ctlr->retrylim++;
+ if(status & TxUnderrun)
+ ctlr->underrun++;
+ if(status & (TxManyCol|TxCollision))
+ ctlr->retrycount++;
+ b = ctlr->txb[ctlr->tdri];
+ if(b == nil)
+ panic("etheremac: bufp");
+ ctlr->txb[ctlr->tdri] = nil;
+ freeb(b);
+ ctlr->ntq--;
+ ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdre);
+ }
+ txstart(ether);
+ unlock(ctlr);
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+ Ether *ether;
+ ulong events;
+ Ctlr *ctlr;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+
+ events = ctlr->regs->isr;
+ eieio();
+ ctlr->regs->isr = events;
+ eieio();
+ ctlr->interrupts++;
+//iprint("eth: %8.8lux\n", events);
+ if(!ctlr->active || events == 0)
+ return;
+
+ if(events & IsrOvr)
+ ctlr->overrun++;
+ if(events & (IsrTe0|IsrTe1))
+ ether->oerrs++;
+
+ rxring(nil, arg);
+ txring(nil, arg);
+ ctlr->interrupts -= 2;
+
+ /* TO DO: restart tx/rx on error */
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+ char *p;
+ int len;
+ Ctlr *ctlr;
+
+ if(n == 0)
+ return 0;
+
+ ctlr = ether->ctlr;
+
+ p = malloc(READSTR);
+ len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
+ len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost);
+ len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat);
+ len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim);
+ len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount);
+ len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll);
+ len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun);
+ len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun);
+ snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred);
+ n = readstr(offset, a, n, p);
+ free(p);
+
+ return n;
+}
+
+static QLock miilock; /* the PHY are both on EMAC0's MII bus */
+
+static int
+miird(Mii *mii, int pa, int ra)
+{
+ Ctlr *ctlr;
+ Emac *em;
+ ulong r;
+ int i;
+
+ if(up)
+ qlock(&miilock);
+ ctlr = mii->ctlr;
+ em = ctlr->miiregs;
+ MIIDBG("r: %x.%x:", pa, ra);
+ if((em->stacr & StaOc) == 0)
+ iprint("mii-not oc\n");
+ em->stacr = StaRead | StaOpb66 | (pa<<5) | ra;
+ for(i=0; i<100 && (em->stacr & StaOc) == 0; i++)
+ microdelay(1);
+ r = em->stacr;
+ if(up)
+ qunlock(&miilock);
+ if((r & StaOc) == 0)
+ iprint("mii'-not oc\n");
+ if(r & StaPhye)
+ return -1;
+ MIIDBG(" %8.8lux\n", r);
+ return r >> 16;
+}
+
+static int
+miiwr(Mii *mii, int pa, int ra, int v)
+{
+ Ctlr *ctlr;
+ Emac *em;
+ ulong r;
+ int i;
+
+ if(up)
+ qlock(&miilock);
+ ctlr = mii->ctlr;
+ em = ctlr->miiregs;
+ if((em->stacr & StaOc) == 0)
+ iprint("miiw-not oc\n");
+ em->stacr = (v<<16) | StaWrite | StaOpb66 | (pa<<5) | ra;
+ for(i=0; i<100 && (em->stacr & StaOc) == 0; i++)
+ microdelay(1);
+ r = em->stacr;
+ if(up)
+ qunlock(&miilock);
+ if((r & StaOc) == 0)
+ iprint("miiw'-not oc\n");
+ if(r & StaPhye)
+ return -1;
+ MIIDBG("w: %x.%x: %8.8lux\n", pa, ra, r);
+ return 0;
+}
+
+static int
+emacmii(Ctlr *ctlr)
+{
+ MiiPhy *phy;
+ int i;
+
+ MIIDBG("mii\n");
+ if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+ return -1;
+ ctlr->mii->ctlr = ctlr;
+ ctlr->mii->mir = miird;
+ ctlr->mii->miw = miiwr;
+
+ if(mii(ctlr->mii, 1<<(ctlr->port+1)) == 0 || (phy = ctlr->mii->curphy) == nil){
+ free(ctlr->mii);
+ ctlr->mii = nil;
+ return -1;
+ }
+
+ iprint("oui %X phyno %d\n", phy->oui, phy->phyno);
+ if(miistatus(ctlr->mii) < 0){
+
+ miireset(ctlr->mii);
+ MIIDBG("miireset\n");
+ if(miiane(ctlr->mii, ~0, 0, ~0) < 0){
+ iprint("miiane failed\n");
+ return -1;
+ }
+ MIIDBG("miistatus...\n");
+ miistatus(ctlr->mii);
+ if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrLs){
+ for(i=0;; i++){
+ if(i > 600){
+ iprint("emac%d: autonegotiation failed\n", ctlr->port);
+ break;
+ }
+ if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrAnc)
+ break;
+ delay(10);
+ }
+ if(miistatus(ctlr->mii) < 0)
+ iprint("miistatus failed\n");
+ }else{
+ iprint("emac%d: no link\n", ctlr->port);
+ phy->speed = 10; /* simple default */
+ }
+ }
+
+ iprint("emac%d mii: fd=%d speed=%d tfc=%d rfc=%d\n", ctlr->port, phy->fd, phy->speed, phy->tfc, phy->rfc);
+
+ MIIDBG("mii done\n");
+
+ return 0;
+}
+
+static void
+emacsetup(Ctlr *ctlr, Ether *ether)
+{
+ int i;
+ Emac *em;
+ ulong mode;
+ MiiPhy *phy;
+
+ /* apparently don't need to set any Alt1 in GPIO */
+
+ em = ctlr->regs;
+
+ /* errata emac_8 */
+ if(em->mr0 & Mr0Rxe){ /* probably never happens in our config */
+ em->mr0 &= ~Mr0Rxe;
+ eieio();
+ for(i=0; (em->mr0 & Mr0Rxi) == 0; i++){
+ if(i > 100){
+ iprint("ethermac: Rxe->Rxi timed out\n");
+ break; /* we'll try soft reset anyway */
+ }
+ microdelay(100);
+ }
+ }
+
+ /* soft reset */
+ em->mr0 = Mr0Srst;
+ eieio();
+ for(i=0; em->mr0 & Mr0Srst; i++){
+ if(i > 20){
+ iprint("ethermac: reset (PHY clocks not running?)");
+ i=0;
+ }
+ microdelay(100);
+ }
+iprint("%d: rx=%8.8lux tx=%8.8lux\n", ctlr->port, PADDR(ctlr->rdr), PADDR(ctlr->tdr));
+//if(ctlr->port)return;
+
+ malrxinit(ctlr->rx, ctlr, Bufsize/16);
+ maltxinit(ctlr->tx, ctlr);
+ malrxreset(ctlr->rx);
+ maltxreset(ctlr->tx);
+
+ em->mr0 = 0;
+ mode = Mr1Rfs4096 | Mr1Tfs2048 | Mr1Tr0mp;
+ if(ctlr->mii != nil && (phy = ctlr->mii->curphy) != nil){
+ if(phy->speed == 10){
+ mode |= Mr1Mf10;
+ if(phy->fd)
+ mode |= Mr1Ist;
+ }else
+ mode |= Mr1Mf100 | Mr1Ist;
+ if(phy->fd)
+ mode |= Mr1Fde;
+ /* errata emac_9 suggests not using integrated flow control (it's broken); so don't negotiate it */
+ if(0 && (phy->rfc || phy->tfc))
+ mode |= Mr1App | Mr1Eifc;
+ ether->mbps = phy->speed;
+ ether->fullduplex = phy->fd;
+ }else{
+ iprint("mii: didn't work: default 100FD\n");
+ mode |= Mr1Mf100 | Mr1Ist | Mr1Fde;
+ ether->mbps = 100;
+ ether->fullduplex = 1;
+ }
+
+ em->mr1 = mode;
+ em->tmr1 = (9<<Tmr1Trl_s) | (256<<Tmr1Tur_s); /* TO DO: validate these sizes */
+ em->rmr = RmrSp | RmrSfcs | RmrIae | RmrBae;
+ em->iahr = (ether->ea[0]<<8) | ether->ea[1];
+ em->ialr = (ether->ea[2]<<24) | (ether->ea[3]<<16) | (ether->ea[4]<<8) | ether->ea[5];
+ em->vtpid = 0;
+ em->vtci = 0;
+ em->ptr = 1; /* pause timer [Reset, T] */
+ for(i=0; i<4; i++){
+ em->iaht[i] = 0; /* individual address hash table */
+ em->gaht[i] = 0; /* group address hash table */
+ }
+ em->ipgvr = (96/8)/3; /* minimise bit times between packets */
+ em->trtr = ((256/64)-1)<<TrtrTrt_s; /* transmission threshold (probably could be smaller) */
+ em->rwmr = (32<<RwmrRlwm_s) | (128<<RwmrRhwm_s); /* receive low/high water mark (TO DO: check) */
+ /* 0x0f002000? */
+ //dumpemac(em);
+ //dumpmal();
+ eieio();
+ em->isr = em->isr; /* clear all events */
+ eieio();
+ em->iser = IsrOvr | IsrBp | IsrSe | IsrSe0 | IsrTe0 | IsrSe1 | IsrTe1; /* enable various error interrupts */
+ /* packet tx/rx interrupts come from MAL */
+ eieio();
+
+ /* tx/rx enable is deferred until attach */
+}
+
+static int
+reset(Ether* ether)
+{
+ uchar ea[Eaddrlen];
+ Ctlr *ctlr;
+ int i;
+
+ ioringreserve(Nrxchan, Nrdre, Ntxchan, Ntdre);
+
+ /*
+ * Insist that the platform-specific code provide the Ethernet address
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+ print("no ether address");
+ return -1;
+ }
+
+ ctlr = malloc(sizeof(*ctlr));
+ ctlr->port = ether->port;
+
+ switch(ether->port){
+ case 0:
+ ctlr->regs = KADDR(PHYSEMAC0);
+ ctlr->miiregs = ctlr->regs;
+ ctlr->rx = malchannel(0, 0, rxring, ether);
+ ctlr->tx = malchannel(0, 1, txring, ether);
+ ether->irq = VectorEMAC0;
+ break;
+ case 1:
+ ctlr->regs = KADDR(PHYSEMAC1);
+ ctlr->miiregs = KADDR(PHYSEMAC0); /* p. 19-41: ``only the MDIO interface for EMAC0 is pinned out'' */
+ ctlr->rx = malchannel(1, 0, rxring, ether);
+ ctlr->tx = malchannel(2, 1, txring, ether);
+ ether->irq = VectorEMAC1;
+ break;
+ default:
+ print("%s ether: no port %lud\n", ether->type, ether->port);
+ free(ctlr);
+ return -1;
+ }
+
+ if(emacmii(ctlr) < 0){
+ free(ctlr);
+ return -1;
+ }
+
+ ether->ctlr = ctlr;
+
+ if(ioringinit(ctlr, Nrdre, Ntdre) < 0) /* TO DO: there are two transmit rings*/
+ panic("etheremac initring");
+
+ for(i = 0; i < ctlr->nrdre; i++){
+ ctlr->rxb[i] = clallocb();
+ ctlr->rdr[i].addr = PADDR(ctlr->rxb[i]->wp);
+ }
+
+ emacsetup(ctlr, ether);
+
+ ether->attach = attach;
+ ether->closed = closed;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt; /* oddly, it's only error interrupts; see malchannel call above for tx/rx */
+ ether->ifstat = ifstat;
+
+ ether->arg = ether;
+ ether->promiscuous = promiscuous;
+ ether->multicast = multicast;
+
+ return 0;
+}
+
+void
+etheremaclink(void)
+{
+ addethercard("EMAC", reset);
+}
+
+static void
+dumpemac(Emac *r)
+{
+ iprint("mr0=%8.8lux\n", r->mr0); /* mode register 0 [see 19-48] */
+ iprint("mr1=%8.8lux\n", r->mr1); /* mode register 1 [Reset] */
+ iprint("tmr0=%8.8lux\n", r->tmr0); /* transmit mode register 0 [see 19-28] */
+ iprint("tmr1=%8.8lux\n", r->tmr1); /* transmit mode register 1 [see 19-28] */
+ iprint("rmr=%8.8lux\n", r->rmr); /* receive mode register [Reset] */
+ iprint("isr=%8.8lux\n", r->isr); /* interrupt status register [Always] */
+ iprint("iser=%8.8lux\n", r->iser); /* interrupt status enable register [Reset] */
+ iprint("iahr=%8.8lux\n", r->iahr); /* individual address high [Reset, R, T]*/
+ iprint("ialr=%8.8lux\n", r->ialr); /* individual address low [Reset, R, T] */
+ iprint("vtpid=%8.8lux\n", r->vtpid); /* VLAN Tag Protocol Identifier [Reset, R, T] */
+ iprint("vtci=%8.8lux\n", r->vtci); /* VLAN Tag Control Information [Reset, R, T] */
+ iprint("ptr=%8.8lux\n", r->ptr); /* pause timer [Reset, T] */
+ iprint("lsah=%8.8lux\n", r->lsah); /* last source address high */
+ iprint("lsal=%8.8lux\n", r->lsal); /* last source address low */
+ iprint("ipgvr=%8.8lux\n", r->ipgvr); /* inter-packet gap value [Reset, T] */
+ iprint("stacr=%8.8lux\n", r->stacr); /* STA control register [see 19-41] */
+ iprint("trtr=%8.8lux\n", r->trtr); /* transmit request threshold register [see 19-42] */
+ iprint("rwmr=%8.8lux\n", r->rwmr); /* receive low/high water mark [Reset] */
+ iprint("octx=%8.8lux\n", r->octx); /* bytes transmitted */
+ iprint("ocrx=%8.8lux\n", r->ocrx); /* bytes received */
+}
diff --git a/os/cerf405/etherif.h b/os/cerf405/etherif.h
new file mode 100644
index 00000000..26f0a764
--- /dev/null
+++ b/os/cerf405/etherif.h
@@ -0,0 +1,37 @@
+enum {
+ MaxEther = 4,
+ Ntypes = 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+RWlock; /* TO DO */
+ ISAConf; /* hardware info */
+ int ctlrno;
+ int tbdf; /* type+busno+devno+funcno */
+ int minmtu;
+ int maxmtu;
+ uchar ea[Eaddrlen];
+ int encry;
+
+ void (*attach)(Ether*); /* filled in by reset routine */
+ void (*closed)(Ether*);
+ 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;
+ int pcmslot; /* PCMCIA */
+ int fullduplex; /* non-zero if full duplex */
+
+ Queue* oq;
+
+ Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern int archether(int, Ether*);
diff --git a/os/cerf405/fns.h b/os/cerf405/fns.h
new file mode 100644
index 00000000..4c6a3fa1
--- /dev/null
+++ b/os/cerf405/fns.h
@@ -0,0 +1,147 @@
+#include "../port/portfns.h"
+
+void addpower(Power*);
+void archbacklight(int);
+void archconfinit(void);
+int archconfval(char**, char**, int);
+void archdisableuart(int);
+void archdisableusb(void);
+void archdisablevideo(void);
+void archenableuart(int, int);
+void archenableusb(int, int);
+void archenablevideo(void);
+void archkbdinit(void);
+void archresetvideo(void);
+void archinit(void);
+int archoptionsw(void);
+void archreboot(void);
+ulong archuartclock(int, int);
+void archuartdma(int, int);
+void clockcheck(void);
+void clockinit(void);
+void clockintr(Ureg*);
+void clrfptrap(void);
+#define coherence() /* nothing needed for uniprocessor */
+void compiledcr(void);
+void cpuidprint(void);
+void* dcflush(void*, ulong);
+void dcinval(void*, ulong);
+void delay(int);
+void dtlbmiss(void);
+void dumplongs(char*, ulong*, int);
+void dumpregs(Ureg*);
+void eieio(void);
+void firmware(int);
+void fpinit(void);
+int fpipower(Ureg*);
+void fpoff(void);
+void fprestore(FPU*);
+void fpsave(FPU*);
+ulong fpstatus(void);
+char* getconf(char*);
+ulong getccr0(void);
+ulong getdar(void);
+ulong getdcr(int);
+ulong getdear(void);
+ulong getdepn(void);
+ulong getdsisr(void);
+ulong getesr(void);
+ulong getimmr(void);
+ulong getmsr(void);
+ulong getpit(void);
+ulong getpvr(void);
+ulong gettbl(void);
+ulong gettbu(void);
+ulong gettsr(void);
+void gotopc(ulong);
+void icflush(void*, ulong);
+void idle(void);
+void idlehands(void);
+int inb(int);
+ulong inl(int);
+int ins(int);
+void insb(int, void*, int);
+void insl(int, void*, int);
+void inss(int, void*, int);
+void intr(Ureg*);
+void intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
+int intrstats(char*, int);
+void intrvec(void);
+void intrcvec(void);
+void ioinit(void);
+void ioreset(void);
+int isaconfig(char*, int, ISAConf*);
+int isvalid_va(void*);
+void itlbmiss(void);
+void kbdinit(void);
+void kbdreset(void);
+void* kmapphys(void*, ulong, ulong, ulong, ulong);
+void lcdpanel(int);
+void links(void);
+void mapfree(RMap*, ulong, int);
+void mapinit(RMap*, Map*, int);
+void mathinit(void);
+void mmuinit(void);
+void* mmucacheinhib(void*, ulong);
+ulong mmumapsize(ulong);
+void pcimapinit(void);
+int pciscan(int, Pcidev **);
+ulong pcibarsize(Pcidev *, int);
+int pcicfgr8(Pcidev*, int);
+int pcicfgr16(Pcidev*, int);
+int pcicfgr32(Pcidev*, int);
+void pcicfgw8(Pcidev*, int, int);
+void pcicfgw16(Pcidev*, int, int);
+void pcicfgw32(Pcidev*, int, int);
+void pciclrbme(Pcidev*);
+void pcihinv(Pcidev*);
+uchar pciipin(Pcidev *, uchar);
+Pcidev* pcimatch(Pcidev*, int, int);
+Pcidev* pcimatchtbdf(int);
+void pcireset(void);
+void pcisetbme(Pcidev*);
+void procsave(Proc*);
+void procsetup(Proc*);
+void putdcr(int, ulong);
+void putesr(ulong);
+void putevpr(ulong);
+void putmsr(ulong);
+void putpit(ulong);
+void puttcr(ulong);
+void puttsr(ulong);
+void puttwb(ulong);
+ulong rmapalloc(RMap*, ulong, int, int);
+long rtctime(void);
+void screeninit(void);
+int screenprint(char*, ...); /* debugging */
+void (*screenputs)(char*, int);
+int segflush(void*, ulong);
+void toggleled(int);
+void setpanic(void);
+ulong _tas(ulong*);
+ulong tlbrehi(int);
+ulong tlbrelo(int);
+int tlbsxcc(void*);
+void tlbwehi(int, ulong);
+void tlbwelo(int, ulong);
+void trapinit(void);
+void trapvec(void);
+void trapcvec(void);
+void uartinstall(void);
+void uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int));
+void uartwait(void); /* debugging */
+void wbflush(void);
+
+#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+ulong getcallerpc(void*);
+
+#define isphys(a) (((ulong)(a)&KSEGM)!=KSEG0 && ((ulong)(a)&KSEGM)!=KSEG1)
+#define KADDR(a) ((void*)((ulong)(a)|KZERO))
+#define PADDR(a) (isphys(a)?(ulong)(a):((ulong)(a)&~KSEGM))
+
+/* IBM bit field order */
+#define IBIT(b) (((ulong)(1<<31))>>(b))
+#define SIBIT(n) ((ushort)1<<(15-(n)))
+#define CIBIT(n) ((uchar)1<<(7-(n)))
+
diff --git a/os/cerf405/fpi.h b/os/cerf405/fpi.h
new file mode 100644
index 00000000..dfb9b1df
--- /dev/null
+++ b/os/cerf405/fpi.h
@@ -0,0 +1,61 @@
+typedef long Word;
+typedef unsigned long Single;
+typedef struct {
+ unsigned long h;
+ unsigned long l;
+} Double;
+
+enum {
+ FractBits = 28,
+ CarryBit = 0x10000000,
+ HiddenBit = 0x08000000,
+ MsBit = HiddenBit,
+ NGuardBits = 3,
+ GuardMask = 0x07,
+ LsBit = (1<<NGuardBits),
+
+ SingleExpBias = 127,
+ SingleExpMax = 255,
+ DoubleExpBias = 1023,
+ DoubleExpMax = 2047,
+
+ ExpBias = DoubleExpBias,
+ ExpInfinity = DoubleExpMax,
+};
+
+typedef struct {
+ unsigned char s;
+ short e;
+ long l; /* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */
+ long h; /* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */
+} Internal;
+
+#define IsWeird(n) ((n)->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/os/cerf405/fpipower.c b/os/cerf405/fpipower.c
new file mode 100644
index 00000000..ec4363b2
--- /dev/null
+++ b/os/cerf405/fpipower.c
@@ -0,0 +1,970 @@
+/*
+ * this doesn't attempt to implement Power architecture 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 "io.h"
+
+#include "ureg.h"
+
+#include "fpi.h"
+
+#define REG(x) (*(long*)(((char*)em->ur)+roff[(x)]))
+#define FR(x) (*(Internal*)em->fr[(x)&0x1F])
+#define REGSP 1 /* stack pointer */
+
+/* BUG: check fetch (not worthwhile in Inferno) */
+#define getulong(a) (*(ulong*)(a))
+
+enum {
+ CRLT = 1<<31,
+ CRGT = 1<<30,
+ CREQ = 1<<29,
+ CRSO = 1<<28,
+ CRFU = CRSO,
+
+ CRFX = 1<<27,
+ CRFEX = 1<<26,
+ CRVX = 1<<25,
+ CROX = 1<<24,
+};
+
+#define getCR(x,w) (((w)>>(28-(x*4)))&0xF)
+#define mkCR(x,v) (((v)&0xF)<<(28-(x*4)))
+
+#define simm(xx, ii) xx = (short)(ii&0xFFFF);
+#define getairr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; simm(imm,i)
+#define getarrr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; rb = (i>>11)&0x1f;
+#define getop(i) ((i>>26)&0x3F)
+#define getxo(i) ((i>>1)&0x3FF)
+
+#define FPS_FX (1<<31) /* exception summary (sticky) */
+#define FPS_EX (1<<30) /* enabled exception summary */
+#define FPS_VX (1<<29) /* invalid operation exception summary */
+#define FPS_OX (1<<28) /* overflow exception OX (sticky) */
+#define FPS_UX (1<<27) /* underflow exception UX (sticky) */
+#define FPS_ZX (1<<26) /* zero divide exception ZX (sticky) */
+#define FPS_XX (1<<25) /* inexact exception XX (sticky) */
+#define FPS_VXSNAN (1<<24) /* invalid operation exception for SNaN (sticky) */
+#define FPS_VXISI (1<<23) /* invalid operation exception for ∞-∞ (sticky) */
+#define FPS_VXIDI (1<<22) /* invalid operation exception for ∞/∞ (sticky) */
+#define FPS_VXZDZ (1<<21) /* invalid operation exception for 0/0 (sticky) */
+#define FPS_VXIMZ (1<<20) /* invalid operation exception for ∞*0 (sticky) */
+#define FPS_VXVC (1<<19) /* invalid operation exception for invalid compare (sticky) */
+#define FPS_FR (1<<18) /* fraction rounded */
+#define FPS_FI (1<<17) /* fraction inexact */
+#define FPS_FPRF (1<<16) /* floating point result class */
+#define FPS_FPCC (0xF<<12) /* <, >, =, unordered */
+#define FPS_VXCVI (1<<8) /* enable exception for invalid integer convert (sticky) */
+#define FPS_VE (1<<7) /* invalid operation exception enable */
+#define FPS_OE (1<<6) /* enable overflow exceptions */
+#define FPS_UE (1<<5) /* enable underflow */
+#define FPS_ZE (1<<4) /* enable zero divide */
+#define FPS_XE (1<<3) /* enable inexact exceptions */
+#define FPS_RN (3<<0) /* rounding mode */
+
+typedef struct Emreg Emreg;
+
+struct Emreg {
+ Ureg* ur;
+ ulong (*fr)[3];
+ FPenv* ufp;
+ ulong ir;
+ char* name;
+};
+
+int 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(r15),
+ OFR(r16), OFR(r17), OFR(r18), OFR(r19),
+ OFR(r20), OFR(r21), OFR(r22), OFR(r23),
+ OFR(r24), OFR(r25), OFR(r26), OFR(r27),
+ OFR(r28), OFR(r29), OFR(r30), OFR(r31),
+};
+
+/*
+ * initial FP register values assumed by qc's code
+ */
+static Internal fpreginit[] = {
+ /* s, e, l, h */
+ {0, 0x400, 0x00000000, 0x08000000}, /* F31=2.0 */
+ {0, 0x3FF, 0x00000000, 0x08000000}, /* F30=1.0 */
+ {0, 0x3FE, 0x00000000, 0x08000000}, /* F29=0.5 */
+ {0, 0x1, 0x00000000, 0x00000000}, /* F28=0.0 */
+ {0, 0x433, 0x00000000, 0x08000040}, /* F27=FREGCVI */
+};
+
+static void
+fadd(Emreg *em, Internal *d, int ra, int rb)
+{
+ Internal a, b;
+
+ a = FR(ra);
+ b = FR(rb);
+ (a.s == b.s? fpiadd: fpisub)(&b, &a, d);
+}
+
+static void
+fsub(Emreg *em, Internal *d, int ra, int rb)
+{
+ Internal a, b;
+
+ a = FR(ra);
+ b = FR(rb);
+ b.s ^= 1;
+ (b.s == a.s? fpiadd: fpisub)(&b, &a, d);
+}
+
+static void
+fmul(Emreg *em, Internal *d, int ra, int rb)
+{
+ Internal a, b;
+
+ a = FR(ra);
+ b = FR(rb);
+ fpimul(&b, &a, d);
+}
+
+static void
+fdiv(Emreg *em, Internal *d, int ra, int rb)
+{
+ Internal a, b;
+
+ a = FR(ra);
+ b = FR(rb);
+ fpidiv(&b, &a, d);
+}
+
+static void
+fmsub(Emreg *em, Internal *d, int ra, int rc, int rb)
+{
+ Internal a, c, b, t;
+
+ a = FR(ra);
+ c = FR(rc);
+ b = FR(rb);
+ fpimul(&a, &c, &t);
+ b.s ^= 1;
+ (b.s == t.s? fpiadd: fpisub)(&b, &t, d);
+}
+
+static void
+fmadd(Emreg *em, Internal *d, int ra, int rc, int rb)
+{
+ Internal a, c, b, t;
+
+ a = FR(ra);
+ c = FR(rc);
+ b = FR(rb);
+ fpimul(&a, &c, &t);
+ (t.s == b.s? fpiadd: fpisub)(&b, &t, d);
+}
+
+static ulong setfpscr(Emreg*);
+static void setfpcc(Emreg*, int);
+
+static void
+unimp(Emreg *em, ulong op)
+{
+ char buf[60];
+
+ snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", em->ur->pc, op);
+ if(fpemudebug)
+ print("FPE: %s\n", buf);
+ error(buf);
+ /* no return */
+}
+
+/*
+ * floating load/store
+ */
+
+static void
+fpeairr(Emreg *em, ulong ir, void **eap, int *rdp)
+{
+ ulong ea;
+ long imm;
+ int ra, rd, upd;
+
+ getairr(ir);
+ ea = imm;
+ upd = (ir&(1L<<26))!=0;
+ if(ra) {
+ ea += REG(ra);
+ if(upd){
+ if(ra == REGSP)
+ panic("fpemu: r1 update"); /* can't do it because we're running on the same stack */
+ REG(ra) = ea;
+ }
+ } else {
+ if(upd)
+ unimp(em, ir);
+ }
+ *rdp = rd;
+ *eap = (void*)ea;
+ if(fpemudebug)
+ print("%8.8lux %s\tf%d,%ld(r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, imm, ra, ea, upd);
+}
+
+static void
+fpearrr(Emreg *em, ulong ir, int upd, void **eap, int *rdp)
+{
+ ulong ea;
+ int ra, rb, rd;
+
+ getarrr(ir);
+ ea = REG(rb);
+ if(ra){
+ ea += REG(ra);
+ if(upd){
+ if(ra == REGSP)
+ panic("fpemu: r1 update");
+ REG(ra) = ea;
+ }
+ if(fpemudebug)
+ print("%8.8lux %s\tf%d,(r%d+r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, ra, rb, ea, upd);
+ } else {
+ if(upd)
+ unimp(em, ir);
+ if(fpemudebug)
+ print("%8.8lux %s\tf%d,(r%d) ea=%lux\n", em->ur->pc, em->name, rd, rb, ea);
+ }
+ *eap = (void*)ea;
+ *rdp = rd;
+}
+
+static void
+lfs(Emreg *em, ulong ir)
+{
+ void *ea;
+ int rd;
+
+ em->name = "lfs";
+ fpeairr(em, ir, &ea, &rd);
+ fpis2i(&FR(rd), (void*)ea);
+}
+
+static void
+lfsx(Emreg *em, ulong ir)
+{
+ void *ea;
+ int rd;
+
+ em->name = "lfsx";
+ fpearrr(em, ir, ((ir>>1)&0x3FF)==567, &ea, &rd);
+ fpis2i(&FR(rd), (void*)ea);
+}
+
+static void
+lfd(Emreg *em, ulong ir)
+{
+ void *ea;
+ int rd;
+
+ em->name = "lfd";
+ fpeairr(em, ir, &ea, &rd);
+ fpid2i(&FR(rd), (void*)ea);
+}
+
+static void
+lfdx(Emreg *em, ulong ir)
+{
+ void *ea;
+ int rd;
+
+ em->name = "lfdx";
+ fpearrr(em, ir, ((ir>>1)&0x3FF)==631, &ea, &rd);
+ fpid2i(&FR(rd), (void*)ea);
+}
+
+static void
+stfs(Emreg *em, ulong ir)
+{
+ void *ea;
+ int rd;
+ Internal tmp;
+
+ em->name = "stfs";
+ fpeairr(em, ir, &ea, &rd);
+ tmp = FR(rd);
+ fpii2s(ea, &tmp);
+}
+
+static void
+stfsx(Emreg *em, ulong ir)
+{
+ void *ea;
+ int rd;
+ Internal tmp;
+
+ em->name = "stfsx";
+ fpearrr(em, ir, getxo(ir)==695, &ea, &rd);
+ tmp = FR(rd);
+ fpii2s(ea, &tmp);
+}
+
+static void
+stfd(Emreg *em, ulong ir)
+{
+ void *ea;
+ int rd;
+ Internal tmp;
+
+ em->name = "stfd";
+ fpeairr(em, ir, &ea, &rd);
+ tmp = FR(rd);
+ fpii2d(ea, &tmp);
+}
+
+static void
+stfdx(Emreg *em, ulong ir)
+{
+ void *ea;
+ int rd;
+ Internal tmp;
+
+ em->name = "stfdx";
+ fpearrr(em, ir, ((ir>>1)&0x3FF)==759, &ea, &rd);
+ tmp = FR(rd);
+ fpii2d(ea, &tmp);
+}
+
+static void
+mcrfs(Emreg *em, ulong ir)
+{
+ int rd, ra, rb;
+ static ulong fpscr0[] ={
+ FPS_FX|FPS_OX,
+ FPS_UX|FPS_ZX|FPS_XX|FPS_VXSNAN,
+ FPS_VXISI|FPS_VXIDI|FPS_VXZDZ|FPS_VXIMZ,
+ FPS_VXVC,
+ 0,
+ FPS_VXCVI,
+ };
+
+ getarrr(ir);
+ if(rb || ra&3 || rd&3)
+ unimp(em, ir);
+ ra >>= 2;
+ rd >>= 2;
+ em->ur->cr = (em->ur->cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, em->ufp->fpscr));
+ em->ufp->fpscr &= ~fpscr0[ra];
+ if(fpemudebug)
+ print("%8.8lux mcrfs\tcrf%d,crf%d\n", em->ur->pc, rd, ra);
+}
+
+static void
+mffs(Emreg *em, ulong ir)
+{
+ int rd, ra, rb;
+ Double dw;
+
+ getarrr(ir);
+ if(ra || rb)
+ unimp(em, ir);
+ dw.h = 0;
+ dw.l = ((uvlong)0xFFF8000L<<16)|em->ufp->fpscr;
+ fpid2i(&FR(rd), &dw);
+ /* it's anyone's guess how CR1 should be set when ir&1 */
+ em->ur->cr &= ~mkCR(1, 0xE); /* leave SO, reset others */
+ if(fpemudebug)
+ print("%8.8lux mffs%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
+}
+
+static void
+mtfsb1(Emreg *em, ulong ir)
+{
+ int rd, ra, rb;
+
+ getarrr(ir);
+ if(ra || rb)
+ unimp(em, ir);
+ em->ufp->fpscr |= (1L << (31-rd));
+ /* BUG: should set summary bits */
+ if(ir & 1)
+ em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */
+ if(fpemudebug)
+ print("%8.8lux mtfsb1%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
+}
+
+static void
+mtfsb0(Emreg *em, ulong ir)
+{
+ int rd, ra, rb;
+
+ getarrr(ir);
+ if(ra || rb)
+ unimp(em, ir);
+ em->ufp->fpscr &= ~(1L << (31-rd));
+ if(ir & 1)
+ em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */
+ if(fpemudebug)
+ print("%8.8lux mtfsb0%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
+}
+
+static void
+mtfsf(Emreg *em, ulong ir)
+{
+ int fm, rb, i;
+ ulong v;
+ Internal b;
+ Double db;
+
+ if(ir & ((1L << 25)|(1L << 16)))
+ unimp(em, ir);
+ rb = (ir >> 11) & 0x1F;
+ fm = (ir >> 17) & 0xFF;
+ b = FR(rb);
+ fpii2d(&db, &b); /* reconstruct hi/lo format to recover low word */
+ v = db.l;
+ for(i=0; i<8; i++)
+ if(fm & (1 << (7-i)))
+ em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v));
+ /* BUG: should set FEX and VX `according to the usual rule' */
+ if(ir & 1)
+ em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */
+ if(fpemudebug)
+ print("%8.8lux mtfsf%s\t#%.2x,fr%d\n", em->ur->pc, ir&1?".":"", fm, rb);
+}
+
+static void
+mtfsfi(Emreg *em, ulong ir)
+{
+ int imm, rd;
+
+ if(ir & ((0x7F << 16)|(1L << 11)))
+ unimp(em, ir);
+ rd = (ir >> 23) & 0xF;
+ imm = (ir >> 12) & 0xF;
+ em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm);
+ /* BUG: should set FEX and VX `according to the usual rule' */
+ if(ir & 1)
+ em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */
+ if(fpemudebug)
+ print("%8.8lux mtfsfi%s\tcrf%d,#%x\n", em->ur->pc, ir&1?".":"", rd, imm);
+}
+
+static void
+fcmp(Emreg *em, ulong ir)
+{
+ int fc, rd, ra, rb, sig, i;
+
+ getarrr(ir);
+ if(rd & 3)
+ unimp(em, ir);
+ rd >>= 2;
+ sig = 0;
+ switch(getxo(ir)) {
+ default:
+ unimp(em, ir);
+ case 32:
+ if(fpemudebug)
+ print("%8.8lux fcmpo\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb);
+ sig = 1;
+ break;
+ case 0:
+ if(fpemudebug)
+ print("%8.8lux fcmpu\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb);
+ break;
+ }
+ if(IsWeird(&FR(ra)) || IsWeird(&FR(rb))) {
+ if(sig){
+ ; /* BUG: should trap if not masked ... */
+ }
+ fc = CRFU;
+ } else {
+ i = fpicmp(&FR(ra), &FR(rb));
+ if(i > 0)
+ fc = CRGT;
+ else if(i == 0)
+ fc = CREQ;
+ else
+ fc = CRLT;
+ }
+ fc >>= 28;
+ em->ur->cr = (em->ur->cr & ~mkCR(rd,~0)) | mkCR(rd, fc);
+ em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (fc<<11);
+ /* BUG: update FX, VXSNAN, VXVC */
+}
+
+static void
+fariths(Emreg *em, ulong ir)
+{
+ int rd, ra, rb, rc, fmt;
+ char *cc, *n;
+ ulong fpscr;
+ Internal *d;
+
+ fmt = 0;
+ rc = (ir>>6)&0x1F;
+ getarrr(ir);
+ d = &FR(rd);
+ switch(getxo(ir)&0x1F) { /* partial XO decode */
+ case 22: /* fsqrts */
+ case 24: /* fres */
+ default:
+ unimp(em, ir);
+ return;
+ case 18:
+ if(IsZero(&FR(rb))) {
+ em->ufp->fpscr |= FPS_ZX | FPS_FX;
+ error("sys: fp: zero divide");
+ }
+ fdiv(em, d, ra, rb);
+ n = "fdivs";
+ break;
+ case 20:
+ fsub(em, d, ra, rb);
+ n = "fsubs";
+ break;
+ case 21:
+ fadd(em, d, ra, rb);
+ n = "fadds";
+ break;
+ case 25:
+ fmul(em, d, ra, rc);
+ rb = rc;
+ n = "fmuls";
+ break;
+ case 28:
+ fmsub(em, d, ra, rc, rb);
+ fmt = 2;
+ n = "fmsubs";
+ break;
+ case 29:
+ fmadd(em, d, ra, rc, rb);
+ fmt = 2;
+ n = "fmadds";
+ break;
+ case 30:
+ fmsub(em, d, ra, rc, rb);
+ d->s ^= 1;
+ fmt = 2;
+ n = "fnmsubs";
+ break;
+ case 31:
+ fmadd(em, d, ra, rc, rb);
+ d->s ^= 1;
+ fmt = 2;
+ n = "fnmadds";
+ break;
+ }
+ if(fmt==1 && ra)
+ unimp(em, ir);
+ fpscr = setfpscr(em);
+ setfpcc(em, rd);
+ cc = "";
+ if(ir & 1) {
+ cc = ".";
+ em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
+ }
+ if(fpemudebug) {
+ switch(fmt) {
+ case 0:
+ print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb);
+ break;
+ case 1:
+ print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
+ break;
+ case 2:
+ print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb);
+ break;
+ }
+ }
+}
+
+static void
+farith(Emreg *em, ulong ir)
+{
+ Word w;
+ Double dv;
+ int rd, ra, rb, rc, fmt;
+ char *cc, *n;
+ ulong fpscr;
+ int nocc;
+ Internal *d;
+
+ fmt = 0;
+ nocc = 0;
+ rc = (ir>>6)&0x1F;
+ getarrr(ir);
+ d = &FR(rd);
+ switch(getxo(ir)&0x1F) { /* partial XO decode */
+ case 22: /* frsqrt */
+ case 23: /* fsel */
+ case 26: /* fsqrte */
+ default:
+ unimp(em, ir);
+ return;
+ case 12: /* frsp */
+ *d = FR(rb); /* BUG: doesn't round to single precision */
+ fmt = 1;
+ n = "frsp";
+ break;
+ case 14: /* fctiw */ /* BUG: ignores rounding mode */
+ case 15: /* fctiwz */
+ fpii2w(&w, &FR(rb));
+ dv.h = 0;
+ dv.l = w;
+ fpid2i(d, &dv);
+ fmt = 1;
+ nocc = 1;
+ n = "fctiw";
+ break;
+ case 18:
+ if(IsZero(&FR(rb))) {
+ em->ufp->fpscr |= FPS_ZX | FPS_FX;
+ error("sys: fp: zero divide");
+ }
+ fdiv(em, d, ra, rb);
+ n = "fdiv";
+ break;
+ case 20:
+ fsub(em, d, ra, rb);
+ n = "fsub";
+ break;
+ case 21:
+ fadd(em, d, ra, rb);
+ n = "fadd";
+ break;
+ case 25:
+ fmul(em, d, ra, rc);
+ rb = rc;
+ n = "fmul";
+ break;
+ case 28:
+ fmsub(em, d, ra, rc, rb);
+ fmt = 2;
+ n = "fmsub";
+ break;
+ case 29:
+ fmadd(em, d, ra, rc, rb);
+ fmt = 2;
+ n = "fmadd";
+ break;
+ case 30:
+ fmsub(em, d, ra, rc, rb);
+ d->s ^= 1;
+ fmt = 2;
+ n = "fnmsub";
+ break;
+ case 31:
+ fmadd(em, d, ra, rc, rb);
+ d->s ^= 1;
+ fmt = 2;
+ n = "fnmadd";
+ break;
+ }
+ if(fmt==1 && ra)
+ unimp(em, ir);
+ fpscr = setfpscr(em);
+ if(nocc == 0)
+ setfpcc(em, rd);
+ cc = "";
+ if(ir & 1) {
+ cc = ".";
+ em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
+ }
+ if(fpemudebug) {
+ switch(fmt) {
+ case 0:
+ print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb);
+ break;
+ case 1:
+ print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
+ break;
+ case 2:
+ print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb);
+ break;
+ }
+ }
+}
+
+static void
+farith2(Emreg *em, ulong ir)
+{
+ int rd, ra, rb;
+ char *cc, *n;
+ ulong fpscr;
+ Internal *d, *b;
+
+ getarrr(ir);
+ if(ra)
+ unimp(em, ir);
+ d = &FR(rd);
+ b = &FR(rb);
+ switch(getxo(ir)) { /* full XO decode */
+ default:
+ unimp(em, ir);
+ case 40:
+ *d = *b;
+ d->s ^= 1;
+ n = "fneg";
+ break;
+ case 72:
+ *d = *b;
+ n = "fmr";
+ break;
+ case 136:
+ *d = *b;
+ d->s = 1;
+ n = "fnabs";
+ break;
+ case 264:
+ *d = *b;
+ d->s = 0;
+ n = "fabs";
+ break;
+ }
+ fpscr = setfpscr(em);
+ setfpcc(em, rd);
+ cc = "";
+ if(ir & 1) {
+ cc = ".";
+ em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
+ }
+ if(fpemudebug)
+ print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
+}
+
+static ulong
+setfpscr(Emreg *em)
+{
+ ulong fps, fpscr;
+
+ fps = 0; /* BUG: getfsr() */
+ fpscr = em->ufp->fpscr;
+ if(fps & FPAOVFL)
+ fpscr |= FPS_OX;
+ if(fps & FPAINEX)
+ fpscr |= FPS_XX;
+ if(fps & FPAUNFL)
+ fpscr |= FPS_UX;
+ if(fps & FPAZDIV)
+ fpscr |= FPS_ZX;
+ if(fpscr != em->ufp->fpscr) {
+ fpscr |= FPS_FX;
+ em->ufp->fpscr = fpscr;
+ }
+ return fpscr;
+}
+
+static void
+setfpcc(Emreg *em, int r)
+{
+ int c;
+ Internal *d;
+
+ d = &FR(r);
+ c = 0;
+ if(IsZero(d))
+ c |= 2;
+ else if(d->s == 1)
+ c |= 4;
+ else
+ c |= 8;
+ if(IsNaN(d))
+ c |= 1;
+ em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */
+}
+
+static uchar op63flag[32] = {
+[12] 1, [14] 1, [15] 1, [18] 1, [20] 1, [21] 1, [22] 1,
+[23] 1, [25] 1, [26] 1, [28] 1, [29] 1, [30] 1, [31] 1,
+};
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpipower(Ureg *ur)
+{
+ ulong op;
+ int xo;
+ Emreg emreg, *em;
+ FPenv *ufp;
+ int n;
+
+ ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */
+ em = &emreg;
+ em->ur = ur;
+ em->fr = ufp->emreg;
+ em->ufp = ufp;
+ em->name = nil;
+ if(em->ufp->fpistate != FPACTIVE) {
+ em->ufp->fpistate = FPACTIVE;
+ em->ufp->fpscr = 0; /* TO DO */
+ for(n = 0; n < nelem(fpreginit); n++)
+ FR(31-n) = fpreginit[n];
+ }
+ for(n=0;;n++){
+ op = getulong(ur->pc);
+ em->ir = op;
+ if(fpemudebug > 1)
+ print("%8.8lux %8.8lux: ", ur->pc, op);
+ switch(op>>26){
+ default:
+ return n;
+ case 48: /* lfs */
+ case 49: /* lfsu */
+ lfs(em, op);
+ break;
+ case 50: /* lfd */
+ case 51: /* lfdu */
+ lfd(em, op);
+ break;
+ case 52: /* stfs */
+ case 53: /* stfsu */
+ stfs(em, op);
+ break;
+ case 54: /* stfd */
+ case 55: /* stfdu */
+ stfd(em, op);
+ break;
+ case 31: /* indexed load/store */
+ xo = getxo(op);
+ if((xo & 0x300) != 0x200)
+ return n;
+ switch(xo){
+ default:
+ return n;
+ case 535: /* lfsx */
+ case 567: /* lfsux */
+ lfsx(em, op);
+ break;
+ case 599: /* lfdx */
+ case 631: /* lfdux */
+ lfdx(em, op);
+ break;
+ case 663: /* stfsx */
+ case 695: /* stfsux */
+ stfsx(em, op);
+ break;
+ case 727: /* stfdx */
+ case 759: /* stfdux */
+ stfdx(em, op);
+ break;
+ }
+ break;
+ case 63: /* double precision */
+ xo = getxo(op);
+ if(op63flag[xo & 0x1F]){
+ farith(em, op);
+ break;
+ }
+ switch(xo){
+ default:
+ return n;
+ case 0: /* fcmpu */
+ case 32: /* fcmpo */
+ fcmp(em, op);
+ break;
+ case 40: /* fneg */
+ case 72: /* fmr */
+ case 136: /* fnabs */
+ case 264: /* fabs */
+ farith2(em, op);
+ break;
+ case 38:
+ mtfsb1(em, op);
+ break;
+ case 64:
+ mcrfs(em, op);
+ break;
+ case 70:
+ mtfsb0(em, op);
+ break;
+ case 134:
+ mtfsfi(em, op);
+ break;
+ case 583:
+ mffs(em, op);
+ break;
+ case 711:
+ mtfsf(em, op);
+ break;
+ }
+ break;
+ case 59: /* single precision */
+ fariths(em, op);
+ break;
+ }
+ ur->pc += 4;
+ if(anyhigher())
+ sched();
+ }
+ return n;
+}
+
+/*
+50: lfd frD,d(rA)
+51: lfdu frD,d(rA)
+31,631: lfdux frD,rA,rB
+31,599: lfdx frD,rA,rB
+48: lfs frD,d(rA)
+49: lfsu frD,d(rA)
+31,567: lfsux frD,rA,rB
+31,535: lfsx frD,rA,rB
+
+54: stfd frS,d(rA)
+55: stfdu frS,d(rA)
+31,759: stfdux frS,rA,rB
+31,727: stfdx frS,rA,rB
+52: stfs frS,d(rA)
+53: stfsu frS,d(rA)
+31,695: stfsux frS,rA,rB
+31,663: stfsx frS,rA,rB
+
+63,64: mcrfs crfD,crfS
+63,583: mffs[.] frD
+63,70: mtfsb0[.] crbD
+63,38: mtfsb1[.] crbD
+63,711: mtfsf[.] FM,frB
+63,134: mtfsfi[.] crfD,IMM
+*/
+
+/*
+float to int:
+ FMOVD g+0(SB),F1
+ FCTIWZ F1,F4
+ FMOVD F4,.rathole+0(SB)
+ MOVW .rathole+4(SB),R7
+ MOVW R7,l+0(SB)
+*/
+
+/*
+int to float:
+ MOVW $1127219200,R9
+ MOVW l+0(SB),R7
+ MOVW R9,.rathole+0(SB)
+ XOR $-2147483648,R7,R6
+ MOVW R6,.rathole+4(SB)
+ FMOVD .rathole+0(SB),F0
+ FSUB F27,F0
+
+unsigned to float:
+ MOVW ul+0(SB),R5
+ MOVW R9,.rathole+0(SB)
+ XOR $-2147483648,R5,R4
+ MOVW R4,.rathole+4(SB)
+ FMOVD .rathole+0(SB),F3
+ FSUB F27,F3
+ FCMPU F3,F28
+ BGE ,3(PC)
+ FMOVD $4.29496729600000000e+09,F2
+ FADD F2,F3
+ FMOVD F3,g+0(SB)
+*/
diff --git a/os/cerf405/gpio.c b/os/cerf405/gpio.c
new file mode 100644
index 00000000..a4821b1c
--- /dev/null
+++ b/os/cerf405/gpio.c
@@ -0,0 +1,106 @@
+#include "u.h"
+#include "mem.h"
+#include "../port/lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define GPIOREGS ((Gpioregs*)KADDR(PHYSGPIO))
+
+static ulong gpioreserved;
+static Lock gpiolock;
+
+void
+gpioreserve(ulong mask)
+{
+ ilock(&gpiolock);
+ if(gpioreserved & mask)
+ panic("gpioreserve: duplicate use of 0x%.8lux", gpioreserved & mask);
+ gpioreserved |= mask;
+ iunlock(&gpiolock);
+}
+
+/*
+ * expand each of the bottom 16 bits into a two bit field
+ * with the bit as low order bit in the field
+ */
+static ulong
+inflate(ulong m)
+{
+ m = ((m & 0xFF00) << 8) | (m & 0x00FF);
+ m = ((m << 4) | m) & 0x0F0F0F0F;
+ m = ((m << 2) | m) & 0x33333333;
+ return ((m << 1) | m) & 0x55555555;
+}
+
+/*
+ * set tcr, osr[hl], tsr[hl], odr, isr1[hl] for gpio bits in m,
+ * following the configuration bits in cfg. when setting
+ * a gpio pin as output, set the right output value in OR first.
+ */
+void
+gpioconfig(ulong m, ulong cfg)
+{
+ Gpioregs *g;
+ ulong h, hm, l, lm;
+
+ h = inflate(m>>16);
+ hm = h | (h<<1);
+ l = inflate(m);
+ lm = l | (l<<1);
+ ilock(&gpiolock);
+ g = GPIOREGS;
+ /*
+ * tsr has a setting ``Alt1 three-state source'' but
+ * table 23-7 sets it to zero (use TCR) and sets TCR.
+ * thus, it seems never really to be needed.
+ */
+ g->tsrh &= ~hm;
+ g->tsrl &= ~lm;
+ /* always select pin input (don't care for outputs) */
+ g->isr1h = (g->isr1h & ~hm) | h;
+ g->isr1l = (g->isr1l & ~lm) | l;
+ if(cfg & Gpio_Alt1){ /* table 23-7 */
+ g->osrh = (g->osrh & ~hm) | h; /* alt1 source */
+ g->osrl = (g->osrl & ~lm) | l;
+ }else{
+ g->osrh &= ~hm; /* GPIO_OR source */
+ g->osrl &= ~lm;
+ }
+ if(cfg & Gpio_OD)
+ g->odr |= m;
+ else
+ g->odr &= ~m;
+ if(cfg & Gpio_in || cfg & Gpio_Tri)
+ g->tcr &= ~m;
+ else
+ g->tcr |= m;
+ iunlock(&gpiolock);
+}
+
+ulong
+gpioget(ulong mask)
+{
+ return GPIOREGS->ir & mask;
+}
+
+void
+gpioset(ulong mask, ulong out)
+{
+ Gpioregs *g;
+
+ ilock(&gpiolock);
+ g = GPIOREGS;
+ g->or = (g->or & ~mask) | (out & mask);
+ iunlock(&gpiolock);
+}
+
+void
+gpiorelease(ulong mask)
+{
+ ilock(&gpiolock);
+ if((gpioreserved & mask) != mask)
+ panic("gpiorelease: unexpected release of 0x%.8lux", ~gpioreserved & mask);
+ gpioreserved &= ~mask;
+ iunlock(&gpiolock);
+}
diff --git a/os/cerf405/iic.c b/os/cerf405/iic.c
new file mode 100644
index 00000000..22d5e51f
--- /dev/null
+++ b/os/cerf405/iic.c
@@ -0,0 +1,605 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+/*
+ * basic read/write interface to 4xx IIC bus (master mode)
+ * ``referred to as IIC to distinguish it from the Phillips I⁲C bus [itself]''
+ *
+ * TO DO:
+ * power management ref count
+ * power up/power down on timer?
+ */
+
+typedef struct Ctlr Ctlr;
+typedef struct IICregs IICregs;
+
+struct IICregs {
+ uchar mdbuf; /* master data buffer */
+ uchar rsvd0;
+ uchar sdbuf; /* slave data buffer */
+ uchar rsvd1;
+ uchar lmadr; /* low master address */
+ uchar hmadr; /* high master address */
+ uchar cntl; /* control */
+ uchar mdcntl; /* mode control */
+ uchar sts; /* status */
+ uchar extsts; /* extended status */
+ uchar lsadr; /* low slave address */
+ uchar hsadr; /* high slave address */
+ uchar clkdiv; /* clock divide */
+ uchar intrmsk; /* interrupt mask */
+ uchar xfrcnt; /* transfer count */
+ uchar xtcntlss; /* extended control and slave status */
+ uchar directcntl; /* direct control */
+};
+
+enum {
+ /* cntl */
+ Hmt= 1<<7, /* halt master transfer */
+ Amd10= 1<<6, /* =0, 7-bit; =1, 10-bit addressing */
+ /* 5,4: two bit transfer count (n-1)&3 bytes */
+ Rpst= 1<<3, /* =0, normal start; =1, repeated Start, transfer should be followed by another start */
+ Cht= 1<<2, /* chain transfer; not the last */
+ Write= 0<<1, /* transfer is a write */
+ Read= 1<<1, /* transfer is a read */
+ Pt= 1<<0, /* =0, most recent transfer complete; =1, start transfer if bus free */
+
+ /* mdcntl */
+ Fsdb= 1<<7, /* flush slave data buffer */
+ Fmdb= 1<<6, /* flush master data buffer */
+ Fsm= 1<<4, /* =0, 100 kHz standard mode; =1, 400 Khz fast mode */
+ Esm= 1<<3, /* enable slave mode */
+ Eint= 1<<2, /* enable interrupt */
+ Eubs= 1<<1, /* exit unknown bus state */
+ Hscl= 1<<0, /* hold IIC serial clock low */
+
+ /* sts */
+ Sss= 1<<7, /* slave status set (slave operation in progress) */
+ Slpr= 1<<6, /* sleep mode */
+ Mdbs= 1<<5, /* master data buffer has data */
+ Mdbf= 1<<4, /* master data buffer is full */
+ Scmp= 1<<3, /* stop complete */
+ Err= 1<<2, /* error set in extsts */
+ Irqa= 1<<1, /* IRQ active */
+ /* Pt as above */
+
+ /* extsts */
+ Irqp= 1<<7, /* IRQ pending */
+ Bcs= 7<<4,
+ Bcs_ssel= 1<<4, /* slave-selected state */
+ Bcs_sio= 2<<4, /* slave transfer state */
+ Bcs_mio= 3<<4, /* master transfer state */
+ Bcs_free= 4<<4, /* bus is free */
+ Bcs_busy= 5<<4, /* bus is busy */
+ Bcs_gok= 6<<4, /* unknown state */
+ Irqd= 1<<3, /* IRQ on deck */
+ La= 1<<2, /* lost arbitration */
+ Ict= 1<<1, /* incomplete transfer */
+ Xfra= 1<<0, /* transfer aborted */
+
+ /* intrmsk */
+ Eirc= 1<<7, /* slave read complete */
+ Eirs= 1<<6, /* slave read needs service */
+ Eiwc= 1<<5, /* slave write complete */
+ Eiws= 1<<4, /* slave write needs service */
+ Eihe= 1<<3, /* halt executed */
+ Eiic= 1<<2, /* incomplete transfer */
+ Eita= 1<<1, /* transfer aborted */
+ Eimtc= 1<<0, /* master transfer complete */
+
+ /* xtcntlss */
+ Src= 1<<7, /* slave read complete; =1, NAK or Stop, or repeated Start ended op */
+ Srs= 1<<6, /* slave read needs service */
+ Swc= 1<<5, /* slave write complete */
+ Sws= 1<<4, /* slave write needs service */
+ Sdbd= 1<<3, /* slave buffer has data */
+ Sdbf= 1<<2, /* slave buffer is full */
+ Epi= 1<<1, /* enable pulsed IRQ on transfer aborted */
+ Srst= 1<<0, /* soft reset */
+
+ /* directcntl */
+ Sdac= 1<<3, /* SDA output */
+ Scc= 1<<2, /* SCL output */
+ Msda= 1<<1, /* SDA input */
+ Msc= 1<<0, /* SCL input */
+
+ /* others */
+ Rbit = 1<<0, /* bit in address byte denoting read */
+ FIFOsize= 4, /* most to be written at once */
+
+ MaxIO = 8192, /* largest transfer done at once (can change) */
+ MaxSA= 2, /* largest subaddress; could be FIFOsize */
+ Bufsize = MaxIO, /* subaddress bytes don't go in buffer */
+ Freq = 100000,
+ I2Ctimeout = 125, /* msec (can change) */
+
+ Chatty = 0,
+};
+
+#define DPRINT if(Chatty)print
+
+/*
+ * I2C software structures
+ */
+
+struct Ctlr {
+ Lock;
+ QLock io;
+ int init;
+ int polling; /* eg, when running before system set up */
+ IICregs* regs; /* hardware registers */
+
+ /* controller state (see below) */
+ int status;
+ int phase;
+ Rendez r;
+
+ /* transfer parameters */
+ int cntl; /* everything but transfer length */
+ int rdcount; /* requested read transfer size */
+ Block* b;
+};
+
+enum {
+ /* Ctlr.state */
+ Idle,
+ Done,
+ Failed,
+ Busy,
+ Halting,
+};
+
+static Ctlr iicctlr[1];
+
+static void interrupt(Ureg*, void*);
+static int readyxfer(Ctlr*);
+static void rxstart(Ctlr*);
+static void txstart(Ctlr*);
+static void stopxfer(Ctlr*);
+static void txoffset(Ctlr*, ulong, int);
+static int idlectlr(Ctlr*);
+
+static void
+iicdump(char *t, IICregs *iic)
+{
+ iprint("iic %s: lma=%.2ux hma=%.2ux im=%.2ux mdcntl=%.2ux sts=%.2ux ests=%.2ux cntl=%.2ux\n",
+ t, iic->lmadr, iic->hmadr, iic->intrmsk, iic->mdcntl, iic->sts, iic->extsts, iic->cntl);
+}
+
+static void
+initialise(IICregs *iic, int intrmsk)
+{
+ int d;
+
+ d = (m->opbhz-1000000)/10000000;
+ if(d <= 0)
+ d = 1; /* just in case OPB freq < 20 Mhz */
+ /* initialisation (see 22.4, p. 22-23) */
+ iic->lmadr = 0;
+ iic->hmadr = 0;
+ iic->sts = Scmp|Irqa;
+ iic->extsts = Irqp | Irqd | La | Ict | Xfra;
+ iic->clkdiv = d;
+ iic->intrmsk = 0; /* see below */
+ iic->xfrcnt = 0;
+ iic->xtcntlss = Src | Srs | Swc | Sws;
+ iic->mdcntl = Fsdb | Fmdb | Eubs; /* reset; standard mode */
+ iic->cntl = 0;
+ eieio();
+ iic->mdcntl = 0;
+ eieio();
+ if(intrmsk){
+ iic->intrmsk = intrmsk;
+ iic->mdcntl = Eint;
+ }
+}
+
+/*
+ * called by the reset routine of any driver using the IIC
+ */
+void
+i2csetup(int polling)
+{
+ IICregs *iic;
+ Ctlr *ctlr;
+
+ ctlr = iicctlr;
+ ctlr->polling = polling;
+ iic = (IICregs*)KADDR(PHYSIIC);
+ ctlr->regs = iic;
+ if(!polling){
+ if(ctlr->init == 0){
+ initialise(iic, Eihe | Eiic | Eita | Eimtc);
+ ctlr->init = 1;
+ intrenable(VectorIIC, interrupt, iicctlr, BUSUNKNOWN, "iic");
+ }
+ }else
+ initialise(iic, 0);
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+ int sts, nb, ext, avail;
+ Ctlr *ctlr;
+ Block *b;
+ IICregs *iic;
+
+ ctlr = arg;
+ iic = ctlr->regs;
+ if(0)
+ iicdump("intr", iic);
+ sts = iic->sts;
+ if(sts & Pt)
+ iprint("iic: unexpected status: %.2ux", iic->sts);
+ ext = iic->extsts;
+ if(sts & Mdbs)
+ nb = iic->xfrcnt & 7;
+ else
+ nb = 0;
+ eieio();
+ iic->sts = sts;
+ if(sts & Err && (ext & (La|Xfra)) != 0)
+ iprint("iic: s=%.2ux es=%.2ux (IO)\n", sts, ext);
+ ctlr->status = ext;
+ switch(ctlr->phase){
+ default:
+ iprint("iic: unexpected interrupt: p-%d s=%.2ux es=%.2ux\n", ctlr->phase, sts, ext);
+ break;
+
+ case Halting:
+ ctlr->phase = Idle;
+ break;
+
+ case Busy:
+ b = ctlr->b;
+ if(b == nil)
+ panic("iic: no buffer");
+ if(ctlr->cntl & Read){
+ /* copy data in from FIFO */
+ avail = b->lim - b->wp;
+ if(nb > avail)
+ nb = avail;
+ while(--nb >= 0)
+ *b->wp++ = iic->mdbuf; /* ``the IIC interface handles the [FIFO] latency'' (22-4) */
+ if(sts & Err || ctlr->rdcount <= 0){
+ ctlr->phase = Done;
+ wakeup(&ctlr->r);
+ break;
+ }
+ rxstart(ctlr);
+ }else{
+ /* account for data transmitted */
+ if((b->rp += nb) > b->wp)
+ b->rp = b->wp;
+ if(sts & Err || BLEN(b) <= 0){
+ ctlr->phase = Done;
+ wakeup(&ctlr->r);
+ break;
+ }
+ txstart(ctlr);
+ }
+ }
+}
+
+static int
+done(void *a)
+{
+ return ((Ctlr*)a)->phase < Busy;
+}
+
+static int
+i2cerror(char *s)
+{
+ DPRINT("iic error: %s\n", s);
+ if(up)
+ error(s);
+ /* no current process, don't call error */
+ return -1;
+}
+
+static char*
+startxfer(I2Cdev *d, int op, void (*xfer)(Ctlr*), Block *b, int n, ulong offset)
+{
+ IICregs *iic;
+ Ctlr *ctlr;
+ int i, cntl, p, s;
+
+ ctlr = iicctlr;
+ if(up){
+ qlock(&ctlr->io);
+ if(waserror()){
+ qunlock(&ctlr->io);
+ nexterror();
+ }
+ }
+ ilock(ctlr);
+ if(!idlectlr(ctlr)){
+ iunlock(ctlr);
+ if(up)
+ error("bus confused");
+ return "bus confused";
+ }
+ if(ctlr->phase >= Busy)
+ panic("iic: ctlr busy");
+ cntl = op | Pt;
+ if(d->tenbit)
+ cntl |= Amd10;
+ ctlr->cntl = cntl;
+ ctlr->b = b;
+ ctlr->rdcount = n;
+ ctlr->phase = Busy;
+ iic = ctlr->regs;
+ if(d->tenbit){
+ iic->hmadr = 0xF0 | (d->addr>>7); /* 2 higher bits of address, LSB don't care */
+ iic->lmadr = d->addr;
+ }else{
+ iic->hmadr = 0;
+ iic->lmadr = d->addr<<1; /* 7-bit address */
+ }
+ if(d->salen)
+ txoffset(ctlr, offset, d->salen);
+ else
+ (*xfer)(ctlr);
+ iunlock(ctlr);
+
+ /* wait for it */
+ if(ctlr->polling){
+ for(i=0; !done(ctlr); i++){
+ delay(2);
+ interrupt(nil, ctlr);
+ }
+ }else
+ tsleep(&ctlr->r, done, ctlr, I2Ctimeout);
+
+ ilock(ctlr);
+ p = ctlr->phase;
+ s = ctlr->status;
+ ctlr->b = nil;
+ if(ctlr->phase != Done && ctlr->phase != Idle)
+ stopxfer(ctlr);
+ iunlock(ctlr);
+
+ if(up){
+ poperror();
+ qunlock(&ctlr->io);
+ }
+ if(p != Done || s & (La|Xfra)){ /* CHECK; time out */
+ if(s & La)
+ return "iic lost arbitration";
+ if(s & Xfra)
+ return "iic transfer aborted";
+ if(p != Done)
+ return "iic timed out";
+ sprint(up->genbuf, "iic error: phase=%d estatus=%.2ux", p, s);
+ return up->genbuf;
+ }
+ return nil;
+}
+
+long
+i2csend(I2Cdev *d, void *buf, long n, ulong offset)
+{
+ Block *b;
+ char *e;
+
+ if(n <= 0)
+ return 0;
+ if(n > MaxIO)
+ n = MaxIO;
+
+ if(up){
+ b = allocb(n);
+ if(b == nil)
+ error(Enomem);
+ if(waserror()){
+ freeb(b);
+ nexterror();
+ }
+ }else{
+ b = iallocb(n);
+ if(b == nil)
+ return -1;
+ }
+ memmove(b->wp, buf, n);
+ b->wp += n;
+ e = startxfer(d, Write, txstart, b, 0, offset);
+ if(up)
+ poperror();
+ n -= BLEN(b); /* residue */
+ freeb(b);
+ if(e)
+ return i2cerror(e);
+ return n;
+}
+
+long
+i2crecv(I2Cdev *d, void *buf, long n, ulong offset)
+{
+ Block *b;
+ long nr;
+ char *e;
+
+ if(n <= 0)
+ return 0;
+ if(n > MaxIO)
+ n = MaxIO;
+
+ if(up){
+ b = allocb(n);
+ if(b == nil)
+ error(Enomem);
+ if(waserror()){
+ freeb(b);
+ nexterror();
+ }
+ }else{
+ b = iallocb(n);
+ if(b == nil)
+ return -1;
+ }
+ e = startxfer(d, Read, rxstart, b, n, offset);
+ nr = BLEN(b);
+ if(nr > 0)
+ memmove(buf, b->rp, nr);
+ if(up)
+ poperror();
+ freeb(b);
+ if(e)
+ return i2cerror(e);
+ return nr;
+}
+
+/*
+ * the controller must be locked for the following functions
+ */
+
+static int
+readyxfer(Ctlr *ctlr)
+{
+ IICregs *iic;
+
+ iic = ctlr->regs;
+ iic->sts = Scmp | Err;
+ if((iic->sts & Pt) != 0){
+ ctlr->phase = Failed;
+ wakeup(&ctlr->r);
+ return 0;
+ }
+ iic->mdcntl |= Fmdb;
+ return 1;
+}
+
+/*
+ * start a master transfer to receive the next chunk of data
+ */
+static void
+rxstart(Ctlr *ctlr)
+{
+ Block *b;
+ int cntl;
+ long nb;
+
+ b = ctlr->b;
+ if(b == nil || (nb = ctlr->rdcount) <= 0){
+ ctlr->phase = Done;
+ wakeup(&ctlr->r);
+ return;
+ }
+ if(!readyxfer(ctlr))
+ return;
+ cntl = ctlr->cntl;
+ if(nb > FIFOsize){
+ nb = FIFOsize;
+ cntl |= Cht; /* more to come */
+ }
+ ctlr->rdcount -= nb;
+ ctlr->regs->cntl = cntl | ((nb-1)<<4);
+}
+
+/*
+ * start a master transfer to send the next chunk of data
+ */
+static void
+txstart(Ctlr *ctlr)
+{
+ Block *b;
+ int cntl, i;
+ long nb;
+ IICregs *iic;
+
+ b = ctlr->b;
+ if(b == nil || (nb = BLEN(b)) <= 0){
+ ctlr->phase = Done;
+ wakeup(&ctlr->r);
+ return;
+ }
+ if(!readyxfer(ctlr))
+ return;
+ cntl = ctlr->cntl;
+ if(nb > FIFOsize){
+ nb = FIFOsize;
+ cntl |= Cht; /* more to come */
+ }
+ iic = ctlr->regs;
+ for(i=0; i<nb; i++)
+ iic->mdbuf = *b->rp++; /* load the FIFO */
+ iic->cntl = cntl | ((nb-1)<<4);
+}
+
+/*
+ * start a master transfer to send a sub-addressing offset;
+ * if subsequently receiving, use Rpst to cause the next transfer to include a Start;
+ * if subsequently sending, use Cht to chain the transfer without a Start.
+ */
+static void
+txoffset(Ctlr *ctlr, ulong offset, int len)
+{
+ int i, cntl;
+ IICregs *iic;
+
+ if(!readyxfer(ctlr))
+ return;
+ iic = ctlr->regs;
+ for(i=len*8; (i -= 8) >= 0;)
+ iic->mdbuf = offset>>i; /* load offset bytes into FIFO */
+ cntl = ctlr->cntl & Amd10;
+ if(ctlr->cntl & Read)
+ cntl |= Rpst;
+ else
+ cntl |= Cht;
+ iic->cntl = cntl | ((len-1)<<4) | Write | Pt;
+}
+
+/*
+ * stop a transfer if one is in progress
+ */
+static void
+stopxfer(Ctlr *ctlr)
+{
+ IICregs *iic;
+ int ext;
+
+ iic = ctlr->regs;
+ ext = iic->extsts;
+ eieio();
+ iic->sts = Scmp | Irqa;
+ eieio();
+ if((iic->sts & Pt) == 0){
+ ctlr->phase = Idle;
+ return;
+ }
+ if((ext & Bcs) == Bcs_mio && ctlr->phase != Halting){
+ ctlr->phase = Halting; /* interrupt will clear the state */
+ iic->cntl = Hmt;
+ }
+}
+
+static int
+idlectlr(Ctlr *ctlr)
+{
+ IICregs *iic;
+
+ iic = ctlr->regs;
+ if((iic->extsts & Bcs) == Bcs_free){
+ if((iic->sts & Pt) == 0){
+ ctlr->phase = Idle;
+ return 1;
+ }
+ iprint("iic: bus free, ctlr busy: s=%.2ux es=%.2ux\n", iic->sts, iic->extsts);
+ }
+ /* hit it with the hammer, soft reset */
+ iprint("iic: soft reset\n");
+ iic->xtcntlss = Srst;
+ iunlock(ctlr);
+ delay(1);
+ ilock(ctlr);
+ initialise(iic, Eihe | Eiic | Eita | Eimtc);
+ ctlr->phase = Idle;
+ return (iic->extsts & Bcs) == Bcs_free && (iic->sts & Pt) == 0;
+}
diff --git a/os/cerf405/inb.s b/os/cerf405/inb.s
new file mode 100644
index 00000000..b664f85e
--- /dev/null
+++ b/os/cerf405/inb.s
@@ -0,0 +1,127 @@
+#include "mem.h"
+
+/*
+ * the tlb entry is configured for little-endian access,
+ * so byte-reversing loads aren't needed
+ */
+
+#define ISAIO PHYSPCIIO0
+
+#define BDNZ BC 16,0,
+
+TEXT inb(SB), $0
+ OR $ISAIO, R3
+ EIEIO
+ MOVBZ (R3), R3
+ RETURN
+
+TEXT insb(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $ISAIO, R3
+ SUB $1, R4
+insb1:
+ EIEIO
+ MOVBZ (R3), R7
+ MOVBU R7, 1(R4)
+ BDNZ insb1
+ RETURN
+
+TEXT outb(SB), $0
+ MOVW v+4(FP), R4
+ OR $ISAIO, R3
+ EIEIO
+ MOVB R4, (R3)
+ RETURN
+
+TEXT outsb(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $ISAIO, R3
+ SUB $1, R4
+outsb1:
+ EIEIO
+ MOVBZU 1(R4), R7
+ MOVB R7, (R3)
+ BDNZ outsb1
+ RETURN
+
+TEXT ins(SB), $0
+ OR $ISAIO, R3
+ EIEIO
+ MOVHZ (R3), R3
+ RETURN
+
+TEXT inss(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $ISAIO, R3
+ SUB $2, R4
+inss1:
+ EIEIO
+ MOVHZ (R3), R7
+ MOVHU R7, 2(R4)
+ BDNZ inss1
+ RETURN
+
+TEXT outs(SB), $0
+ MOVW v+4(FP), R4
+ OR $ISAIO, R3
+ EIEIO
+ MOVH R4, (R3)
+ RETURN
+
+TEXT outss(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $ISAIO, R3
+ SUB $2, R4
+outss1:
+ EIEIO
+ MOVHZU 2(R4), R7
+ MOVH R7, (R3)
+ BDNZ outss1
+ RETURN
+
+TEXT inl(SB), $0
+ OR $ISAIO, R3
+ EIEIO
+ MOVW (R3), R3
+ RETURN
+
+TEXT insl(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $ISAIO, R3
+ SUB $4, R4
+insl1:
+ EIEIO
+ MOVW (R3), R7
+ MOVWU R7, 4(R4)
+ BDNZ insl1
+ RETURN
+
+TEXT outl(SB), $0
+ MOVW v+4(FP), R4
+ OR $ISAIO, R3
+ EIEIO
+ MOVW R4, (R3)
+ RETURN
+
+TEXT outsl(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $ISAIO, R3
+ SUB $4, R4
+outsl1:
+ EIEIO
+ MOVWU 4(R4), R7
+ MOVW R7, (R3)
+ BDNZ outsl1
+ RETURN
diff --git a/os/cerf405/io.h b/os/cerf405/io.h
new file mode 100644
index 00000000..1f779dfc
--- /dev/null
+++ b/os/cerf405/io.h
@@ -0,0 +1,301 @@
+typedef struct BD BD;
+typedef struct Ring Ring;
+typedef struct MALdev MALdev;
+typedef struct I2Cdev I2Cdev;
+
+enum
+{
+ /* 405EP UIC interrupt vectors (IBM bit numbering) */
+ VectorUIC= 0,
+ VectorUART0=VectorUIC,
+ VectorUART1,
+ VectorIIC,
+ VectorPCIECW,
+ VectorRsvd1,
+ VectorDMA0,
+ VectorDMA1,
+ VectorDMA2,
+ VectorDMA3,
+ VectorEtherwake,
+ VectorMALSERR,
+ VectorMALTXEOB,
+ VectorMALRXEOB,
+ VectorMALTXDE,
+ VectorMALRXDE,
+ VectorEMAC0,
+ VectorPCISERR,
+ VectorEMAC1,
+ VectorPCIPM,
+ VectorGPT0,
+ VectorGPT1,
+ VectorGPT2,
+ VectorGPT3,
+ VectorGPT4,
+ /* 1 reserved */
+ VectorIRQ= VectorUIC+25, /* IRQ0 to IRQ6 */
+ MaxVector= VectorIRQ+7,
+
+ /* some flags to change polarity and sensitivity */
+ IRQmask= 0xFF, /* actual vector address */
+ IRQactivelow= 1<<8,
+ IRQedge= 1<<9,
+ IRQcritical= 1<<10,
+};
+
+/*
+ * these are defined to keep the interface compatible with other
+ * architectures, but only BUSUNKNOWN is currently used
+ */
+#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf) (((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf) ((tbdf)>>24)
+#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN (-1)
+
+enum {
+ BusOPB,
+ BusPLB,
+ BusPCI,
+ MaxBus
+};
+
+/*
+ * MAL Buffer Descriptors and IO Rings
+ */
+
+struct BD {
+ ushort status;
+ ushort length;
+ ulong addr;
+};
+#define MAXIORING 256 /* hardware limit to ring size */
+#define BDBUFLIM (4096-16) /* no MAL buffer larger than this */
+
+BD* bdalloc(ulong);
+void bdfree(BD*, int);
+void dumpbd(char*, BD*, int);
+
+enum {
+ /* Rx BDs, bits common to all protocols */
+ BDEmpty= 1<<15,
+ BDWrap= 1<<14, /* end of ring */
+ BDContin= 1<<13, /* continuous mode */
+ BDLast= 1<<12, /* last buffer in current packet */
+ BDFirst= 1<<11, /* first buffer in current packet (set by MAL) */
+ BDInt= 1<<10, /* interrupt when done */
+
+ /* Tx BDs */
+ BDReady= 1<<15, /* ready to transmit; set by driver, cleared by MAL */
+ /* BDWrap, BDInt, BDLast as above */
+};
+
+struct Ring {
+ BD* rdr; /* receive descriptor ring */
+ Block** rxb; /* receive ring buffers */
+ int rdrx; /* index into rdr */
+ int nrdre; /* length of rdr */
+
+ BD* tdr; /* transmit descriptor ring */
+ Block** txb; /* transmit ring buffers */
+ int tdrh; /* host index into tdr */
+ int tdri; /* interface index into tdr */
+ int ntdre; /* length of tdr */
+ int ntq; /* pending transmit requests */
+};
+
+#define NEXT(x, l) (((x)+1)%(l))
+#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1)
+#define HOWMANY(x, y) (((x)+((y)-1))/(y))
+#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y))
+
+/*
+ * one per mal channel
+ */
+typedef struct Mal Mal;
+struct Mal {
+ int n;
+ int len;
+ int tx;
+ ulong mask;
+
+ void* arg;
+ void (*interrupt)(Ureg*, void*);
+};
+
+Mal* malchannel(int, int, void (*)(Ureg*, void*), void*);
+void maltxreset(Mal*);
+void maltxinit(Mal*, Ring*);
+void maltxenable(Mal*);
+void malrxreset(Mal*);
+void malrxinit(Mal*, Ring*, ulong);
+void malrxenable(Mal*);
+void ioringreserve(int, ulong, int, ulong);
+int ioringinit(Ring*, int, int);
+
+typedef struct Gpioregs Gpioregs;
+struct Gpioregs {
+ ulong or; /* output register */
+ ulong tcr; /* tristate control */
+ ulong osrh; /* output select high (0-15) */
+ ulong osrl; /* output select low (16-31) */
+ ulong tsrh; /* tristate select high (0-15) */
+ ulong tsrl; /* tristate select low (16-31) */
+ ulong odr; /* open drain */
+ ulong ir; /* input */
+ ulong rr1; /* receive register */
+ ulong pad[3];
+ ulong isr1h; /* input select 1 high (0-15) */
+ ulong isr1l; /* input select 1 low (16-31) */
+};
+
+enum {
+ /* software configuration bits for gpioconfig */
+ Gpio_Alt1= 1<<0, /* implies specific settings of all the others, but include in or out */
+ Gpio_OD= 1<<1,
+ Gpio_Tri= 1<<2,
+ Gpio_in= 1<<4,
+ Gpio_out= 1<<5,
+};
+
+void gpioreserve(ulong);
+void gpioconfig(ulong, ulong);
+ulong gpioget(ulong);
+void gpioset(ulong, ulong);
+void gpiorelease(ulong);
+
+/*
+ * used by ../port/devi2c.c and iic.c
+ */
+struct I2Cdev {
+ int addr;
+ int salen; /* length in bytes of subaddress, if used; 0 otherwise */
+ int tenbit; /* 10-bit addresses */
+};
+
+long i2crecv(I2Cdev*, void*, long, ulong);
+long i2csend(I2Cdev*, void*, long, ulong);
+void i2csetup(int);
+
+/*
+ * PCI support code.
+ */
+enum { /* type 0 and type 1 pre-defined header */
+ PciVID = 0x00, /* vendor ID */
+ PciDID = 0x02, /* device ID */
+ PciPCR = 0x04, /* command */
+ PciPSR = 0x06, /* status */
+ PciRID = 0x08, /* revision ID */
+ PciCCRp = 0x09, /* programming interface class code */
+ PciCCRu = 0x0A, /* sub-class code */
+ PciCCRb = 0x0B, /* base class code */
+ PciCLS = 0x0C, /* cache line size */
+ PciLTR = 0x0D, /* latency timer */
+ PciHDT = 0x0E, /* header type */
+ PciBST = 0x0F, /* BIST */
+
+ PciBAR0 = 0x10, /* base address */
+ PciBAR1 = 0x14,
+
+ PciINTL = 0x3C, /* interrupt line */
+ PciINTP = 0x3D, /* interrupt pin */
+};
+
+enum { /* type 0 pre-defined header */
+ PciBAR2 = 0x18,
+ PciBAR3 = 0x1C,
+ PciBAR4 = 0x20,
+ PciBAR5 = 0x24,
+ PciCIS = 0x28, /* cardbus CIS pointer */
+ PciSVID = 0x2C, /* subsystem vendor ID */
+ PciSID = 0x2E, /* cardbus CIS pointer */
+ PciEBAR0 = 0x30, /* expansion ROM base address */
+ PciMGNT = 0x3E, /* burst period length */
+ PciMLT = 0x3F, /* maximum latency between bursts */
+};
+
+enum { /* type 1 pre-defined header */
+ PciPBN = 0x18, /* primary bus number */
+ PciSBN = 0x19, /* secondary bus number */
+ PciUBN = 0x1A, /* subordinate bus number */
+ PciSLTR = 0x1B, /* secondary latency timer */
+ PciIBR = 0x1C, /* I/O base */
+ PciILR = 0x1D, /* I/O limit */
+ PciSPSR = 0x1E, /* secondary status */
+ PciMBR = 0x20, /* memory base */
+ PciMLR = 0x22, /* memory limit */
+ PciPMBR = 0x24, /* prefetchable memory base */
+ PciPMLR = 0x26, /* prefetchable memory limit */
+ PciPUBR = 0x28, /* prefetchable base upper 32 bits */
+ PciPULR = 0x2C, /* prefetchable limit upper 32 bits */
+ PciIUBR = 0x30, /* I/O base upper 16 bits */
+ PciIULR = 0x32, /* I/O limit upper 16 bits */
+ PciEBAR1 = 0x28, /* expansion ROM base address */
+ PciBCR = 0x3E, /* bridge control register */
+};
+
+enum { /* type 2 pre-defined header */
+ PciCBExCA = 0x10,
+ PciCBSPSR = 0x16,
+ PciCBPBN = 0x18, /* primary bus number */
+ PciCBSBN = 0x19, /* secondary bus number */
+ PciCBUBN = 0x1A, /* subordinate bus number */
+ PciCBSLTR = 0x1B, /* secondary latency timer */
+ PciCBMBR0 = 0x1C,
+ PciCBMLR0 = 0x20,
+ PciCBMBR1 = 0x24,
+ PciCBMLR1 = 0x28,
+ PciCBIBR0 = 0x2C, /* I/O base */
+ PciCBILR0 = 0x30, /* I/O limit */
+ PciCBIBR1 = 0x34, /* I/O base */
+ PciCBILR1 = 0x38, /* I/O limit */
+ PciCBSVID = 0x40, /* subsystem vendor ID */
+ PciCBSID = 0x42, /* subsystem ID */
+ PciCBLMBAR = 0x44, /* legacy mode base address */
+};
+
+typedef struct Pcisiz Pcisiz;
+struct Pcisiz
+{
+ Pcidev* dev;
+ int siz;
+ int bar;
+};
+
+typedef struct Pcidev Pcidev;
+struct Pcidev
+{
+ int tbdf; /* type+bus+device+function */
+ ushort vid; /* vendor ID */
+ ushort did; /* device ID */
+
+ uchar rid;
+ uchar ccrp;
+ uchar ccru;
+ uchar ccrb;
+
+ struct {
+ ulong bar; /* base address */
+ int size;
+ } mem[6];
+
+ struct {
+ ulong bar;
+ int size;
+ } rom;
+ uchar intl; /* interrupt line */
+
+ Pcidev* list;
+ Pcidev* link; /* next device on this bno */
+
+ Pcidev* bridge; /* down a bus */
+ struct {
+ ulong bar;
+ int size;
+ } ioa, mema;
+ ulong pcr;
+};
+
+#define PCIWINDOW 0x80000000
+#define PCIWADDR(va) (PADDR(va)+PCIWINDOW)
diff --git a/os/cerf405/l.s b/os/cerf405/l.s
new file mode 100644
index 00000000..28990ef6
--- /dev/null
+++ b/os/cerf405/l.s
@@ -0,0 +1,795 @@
+#include "mem.h"
+
+#define MB (1024*1024)
+
+/*
+ * common ppc special purpose registers
+ */
+#define DSISR 18
+#define SRR0 26 /* Saved Registers (exception) */
+#define SRR1 27
+#define SPRG0 272 /* Supervisor Private Registers */
+#define SPRG1 273
+#define SPRG2 274
+#define SPRG3 275
+#define TBRU 269 /* Time base Upper/Lower (Reading) */
+#define TBRL 268
+#define TBWU 285 /* Time base Upper/Lower (Writing) */
+#define TBWL 284
+#define PVR 287 /* Processor Version */
+
+/*
+ * 4xx-specific special purpose registers of interest here
+ */
+#define ICCR 1019 /* instruction cache control */
+#define DCCR 1018 /* data cache control */
+#define DBCR0 1010 /* debug control register 0 */
+#define DCWR 964 /* data cache write-through */
+#define PID 945 /* TLB process ID */
+#define CCR0 947 /* core configuration register 0 */
+#define SLER 955 /* storage little-endian */
+#define SU0R 956 /* storage user-defined 0 */
+#define SRR2 990
+#define SRR3 991
+/* SPRGn up to 7, if needed, on the 400 series */
+#define DEAR 961 /* data error address */
+#define ESR 980 /* exception syndrome */
+#define EVPR 982 /* exception vector prefix */
+#define PIT 987 /* interval timer */
+#define SGR 953 /* storage guarded */
+#define TCR 986 /* timer control */
+#define TSR 984 /* timer status */
+#define ZPR 944 /* zone protection */
+
+/*
+ * 4xx-specific(?) device control registers
+ */
+#define OCM0_DSCNTL 0x1B /* OCM data-side control register */
+
+/* use of SPRG registers in save/restore */
+#define SAVER0 SPRG0
+#define SAVER1 SPRG1
+#define SAVELR SPRG2
+#define SAVEXX SPRG3
+
+/* special instruction definitions */
+#define BDNZ BC 16,0,
+#define BDNE BC 0,2,
+#define TLBIA WORD $((31<<26)|(370<<1))
+#define TLBSYNC WORD $((31<<26)|(566<<1))
+#define MFTB(tbr,d) WORD $((31<<26)|((d)<<21)|((tbr&0x1f)<<16)|(((tbr>>5)&0x1f)<<11)|(371<<1))
+
+/* 603/603e specific: load tlb entries */
+#define TLBLD(x) WORD $((31<<26)|(978<<1)|((x&0x1F)<<11))
+#define TLBLI(x) WORD $((31<<26)|(1010<<1)|((x&0x1F)<<11))
+
+/* 400 models; perhaps others */
+#define ICCCI(a,b) WORD $((31<<26)|((a)<<16)|((b)<<11)|(966<<1))
+#define DCCCI(a,b) WORD $((31<<26)|((a)<<16)|((b)<<11)|(454<<1))
+/* these follow the source -> dest ordering */
+#define DCREAD(s,t) WORD $((31<<26)|((t)<<21)|((s)<<11)|(486<<1))
+#define DCRF(n) ((((n)>>5)&0x1F)|(((n)&0x1F)<<5))
+#define MTDCR(s,n) WORD $((31<<26)|((s)<<21)|(DCRF(n)<<11)|(451<<1))
+#define MFDCR(n,t) WORD $((31<<26)|((t)<<21)|(DCRF(n)<<11)|(323<<1))
+#define TLBRELO(a,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|(1<<11)|(946<<1))
+#define TLBREHI(a,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|(0<<11)|(946<<1))
+#define TLBWELO(s,a) WORD $((31<<26)|((s)<<21)|((a)<<16)|(1<<11)|(978<<1))
+#define TLBWEHI(s,a) WORD $((31<<26)|((s)<<21)|((a)<<16)|(0<<11)|(978<<1))
+#define TLBSX(a,b,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1))
+#define TLBSXCC(a,b,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1)|1)
+#define WRTMSR_EE(s) WORD $((31<<26)|((s)<<21)|(131<<1))
+#define WRTMSR_EEI(e) WORD $((31<<26)|((e)<<16)|(163<<1))
+
+/* on some models mtmsr doesn't synchronise enough (eg, 603e) */
+#define MSRSYNC SYNC; ISYNC
+
+/* on the 400 series, the prefetcher madly fetches across RFI, sys call, and others; use BR 0(PC) to stop */
+#define RFI WORD $((19<<26)|(50<<1)); BR 0(PC)
+#define RFCI WORD $((19<<26)|(51<<1)); BR 0(PC)
+
+#define UREGSPACE (UREGSIZE+8)
+
+/* could define STEP to set an LED to mark progress */
+#define STEP(x)
+
+/*
+ * Boot first processor
+ */
+ TEXT start(SB), $-4
+
+ MOVW MSR, R3
+ RLWNM $0, R3, $~MSR_EE, R3
+ OR $MSR_ME, R3
+ ISYNC
+ MOVW R3, MSR /* turn off interrupts but enable traps */
+ MSRSYNC
+ MOVW $0, R0 /* except during trap handling, R0 is zero from now on */
+ MOVW R0, CR
+
+ MOVW $setSB-KZERO(SB), R2 /* SB until mmu on */
+
+/*
+ * reset the caches and disable them until mmu on
+ */
+ MOVW R0, SPR(ICCR)
+ ICCCI(0, 2) /* the errata reveals that EA is used; we'll use SB */
+ ISYNC
+
+ MOVW $((CACHEWAYSIZE/CACHELINESZ)-1), R3
+ MOVW R3, CTR
+ MOVW R0, R3
+dcinv:
+ DCCCI(0,3)
+ ADD $32, R3
+ BDNZ dcinv
+
+ /* cache is copy-back, disabled; no user-defined 0; big endian throughout */
+ MOVW R0, SPR(DCWR)
+ MOVW R0, SPR(DCCR)
+ MOVW R0, SPR(SU0R)
+ MOVW R0, SPR(SLER)
+ ISYNC
+
+ /* guard everything above 0x20000000 */
+ MOVW $~(0xF000<<16), R3
+ MOVW R3, SPR(SGR)
+ ISYNC
+
+ /* set access to LED */
+ MOVW $PHYSGPIO, R4
+ MOVW $(1<<31), R6
+ MOVW $(0xC000<<16), R5
+ MOVW 4(R4), R3
+ OR R6, R3
+ MOVW R3, 4(R4) /* tcr set */
+ MOVW 0x18(R4), R3
+ ANDN R6, R3
+ MOVW R3, 0x18(R4) /* odr reset */
+ MOVW 8(R4), R3
+ ANDN R5, R3
+ MOVW R3, 8(R4) /* osrh uses or */
+ MOVW 0x10(R4), R3
+ ANDN R5, R3
+ MOVW R3, 0x10(R4) /* tsr uses tcr */
+
+ MOVW $(1<<31), R4 /* reset MAL */
+ MTDCR(0x180, 4)
+
+/*
+ MOVW $'H', R3
+ BL uartputc(SB)
+ MOVW $'\n', R3
+ BL uartputc(SB)
+*/
+
+/*
+ * set other system configuration values
+ */
+ MOVW R0, SPR(PIT)
+ MOVW $~0, R3
+ MOVW R3, SPR(TSR)
+
+STEP(1)
+
+ BL kernelmmu(SB)
+ /* now running with correct addresses, mmu on */
+
+ MOVW $setSB(SB), R2
+
+ /* enable caches for kernel 128mb in real mode; data is copy-back */
+ MOVW R0, SPR(DCWR)
+ MOVW $(1<<31), R3
+ MOVW R3, SPR(DCCR)
+ MOVW R3, SPR(ICCR)
+
+/*
+ BL ledoff(SB)
+
+ MOVW $0x800, R8
+ MOVW R8, LR
+ BL (LR)
+ BR 0(PC)
+*/
+
+STEP(2)
+ /* no kfpinit on 4xx */
+
+ MOVW $mach0(SB), R(MACH)
+ ADD $(MACHSIZE-8), R(MACH), R1
+ SUB $4, R(MACH), R3
+ ADD $4, R1, R4
+clrmach:
+ MOVWU R0, 4(R3)
+ CMP R3, R4
+ BNE clrmach
+
+ MOVW R0, R(USER)
+ MOVW R0, 0(R(MACH))
+
+ MOVW $edata(SB), R3
+ MOVW $end(SB), R4
+ ADD $4, R4
+ SUB $4, R3
+clrbss:
+ MOVWU R0, 4(R3)
+ CMP R3, R4
+ BNE clrbss
+
+STEP(3)
+ BL main(SB)
+ BR 0(PC)
+
+TEXT kernelmmu(SB), $-4
+ TLBIA
+ ISYNC
+ SYNC
+
+ /* make following TLB entries shared, TID=PID=0 */
+ MOVW R0, SPR(PID)
+
+ /* all zones are supervisor, access controlled by TLB */
+ MOVW R0, SPR(ZPR)
+
+ /* map various things 1:1 */
+ MOVW $tlbtab-KZERO(SB), R4
+ MOVW $tlbtabe-KZERO(SB), R5
+ SUB R4, R5
+ MOVW $(2*4), R6
+ DIVW R6, R5
+ SUB $4, R4
+ MOVW R5, CTR
+ MOVW R0, R3
+ltlb:
+ MOVWU 4(R4), R5 /* TLBHI */
+ TLBWEHI(5,3)
+ MOVWU 4(R4), R5 /* TLBLO */
+ TLBWELO(5,3)
+ ADD $1, R3
+ BDNZ ltlb
+
+ MOVW LR, R3
+ OR $KZERO, R3
+ MOVW R3, SPR(SRR0)
+ MOVW MSR, R4
+ OR $(MSR_IR|MSR_DR), R4
+ MOVW R4, SPR(SRR1)
+
+ RFI /* resume in kernel mode in caller */
+
+TEXT ledoff(SB), $0
+ MOVW $PHYSGPIO, R4
+ MOVW 0(R4), R3
+ RLWNM $0, R3, $~(1<<31), R3 /* LED off */
+ MOVW R3, 0(R4)
+ RETURN
+
+TEXT splhi(SB), $0
+ MOVW MSR, R3
+ RLWNM $0, R3, $~MSR_EE, R4
+ SYNC
+ MOVW R4, MSR
+ MSRSYNC
+ MOVW LR, R31
+ MOVW R31, 4(R(MACH)) /* save PC in m->splpc */
+ RETURN
+
+TEXT splx(SB), $0
+ MOVW MSR, R4
+ RLWMI $0, R3, $MSR_EE, R4
+ RLWNMCC $0, R3, $MSR_EE, R5
+ BNE splx0
+ MOVW LR, R31
+ MOVW R31, 4(R(MACH)) /* save PC in m->splpc */
+splx0:
+ SYNC
+ MOVW R4, MSR
+ MSRSYNC
+ RETURN
+
+TEXT splxpc(SB), $0
+ MOVW MSR, R4
+ RLWMI $0, R3, $MSR_EE, R4
+ RLWNMCC $0, R3, $MSR_EE, R5
+ SYNC
+ MOVW R4, MSR
+ MSRSYNC
+ RETURN
+
+TEXT spllo(SB), $0
+ MFTB(TBRL, 3)
+ MOVW R3, spltbl(SB)
+ MOVW MSR, R3
+ OR $MSR_EE, R3, R4
+ SYNC
+ MOVW R4, MSR
+ MSRSYNC
+ RETURN
+
+TEXT spldone(SB), $0
+ RETURN
+
+TEXT islo(SB), $0
+ MOVW MSR, R3
+ RLWNM $0, R3, $MSR_EE, R3
+ RETURN
+
+TEXT setlabel(SB), $-4
+ MOVW LR, R31
+ MOVW R1, 0(R3)
+ MOVW R31, 4(R3)
+ MOVW $0, R3
+ RETURN
+
+TEXT gotolabel(SB), $-4
+ MOVW 4(R3), R31
+ MOVW R31, LR
+ MOVW 0(R3), R1
+ MOVW $1, R3
+ RETURN
+
+TEXT tlbwelo(SB), $-4
+ MOVW v+4(FP), R5
+ SYNC
+ TLBWELO(5, 3)
+ ISYNC
+ SYNC
+ RETURN
+
+TEXT tlbwehi(SB), $-4
+ MOVW v+4(FP), R5
+ SYNC
+ TLBWEHI(5, 3)
+ ISYNC
+ SYNC
+ RETURN
+
+TEXT tlbrehi(SB), $-4
+ TLBREHI(3, 3)
+ RETURN
+
+TEXT tlbrelo(SB), $-4
+ TLBRELO(3, 3)
+ RETURN
+
+TEXT tlbsxcc(SB), $-4
+ TLBSXCC(0, 3, 3)
+ BEQ tlbsxcc0
+ MOVW $-1, R3 /* not found */
+tlbsxcc0:
+ RETURN
+
+/*
+ * enter with stack set and mapped.
+ * on return, SB (R2) has been set, and R3 has the Ureg*,
+ * the MMU has been re-enabled, kernel text and PC are in KSEG,
+ * R(MACH) has been set, and R0 contains 0.
+ *
+ * this can be simplified in the Inferno regime
+ */
+TEXT saveureg(SB), $-4
+/*
+ * save state
+ */
+ MOVMW R2, 48(R1) /* r2:r31 */
+ MOVW $setSB(SB), R2
+ MOVW SPR(SAVER1), R4
+ MOVW R4, 44(R1)
+ MOVW SPR(SAVER0), R5
+ MOVW R5, 40(R1)
+ MOVW CTR, R6
+ MOVW R6, 36(R1)
+ MOVW XER, R4
+ MOVW R4, 32(R1)
+ MOVW CR, R5
+ MOVW R5, 28(R1)
+ MOVW SPR(SAVELR), R6 /* LR */
+ MOVW R6, 24(R1)
+ /* pad at 20(R1) */
+ /* old PC(16) and status(12) saved earlier */
+ MOVW SPR(SAVEXX), R0
+ MOVW R0, 8(R1) /* cause/vector */
+ ADD $8, R1, R3 /* Ureg* */
+ STWCCC R3, (R1) /* break any pending reservations */
+ MOVW $0, R0 /* compiler/linker expect R0 to be zero */
+
+ MOVW MSR, R5
+ OR $(MSR_IR|MSR_DR), R5 /* enable MMU */
+ MOVW R5, SPR(SRR1)
+ MOVW LR, R31
+ OR $KZERO, R31 /* return PC in KSEG0 */
+ MOVW R31, SPR(SRR0)
+ SYNC
+ ISYNC
+ RFI /* returns to trap handler */
+
+TEXT icflush(SB), $-4 /* icflush(virtaddr, count) */
+ MOVW n+4(FP), R4
+ CMP R4, R0
+ BLE icf1
+ RLWNM $0, R3, $~(CACHELINESZ-1), R5
+ SUB R5, R3
+ ADD R3, R4
+ ADD $(CACHELINESZ-1), R4
+ SRAW $CACHELINELOG, R4
+ MOVW R4, CTR
+icf0: ICBI (R5)
+ ADD $CACHELINESZ, R5
+ BDNZ icf0
+icf1:
+ ISYNC
+ RETURN
+
+/*
+ * flush to store and invalidate globally
+ */
+TEXT dcflush(SB), $-4 /* dcflush(virtaddr, count) */
+ SYNC
+ MOVW n+4(FP), R4
+ RLWNM $0, R3, $~(CACHELINESZ-1), R5
+ CMP R4, $0
+ BLE dcf1
+ SUB R5, R3
+ ADD R3, R4
+ ADD $(CACHELINESZ-1), R4
+ SRAW $CACHELINELOG, R4
+ MOVW R4, CTR
+dcf0: DCBF (R5)
+ ADD $CACHELINESZ, R5
+ BDNZ dcf0
+ SYNC
+dcf1:
+ ISYNC
+ MOVW R5, R3 /* check its operation */
+ RETURN
+
+/*
+ * invalidate without flush, globally
+ */
+TEXT dcinval(SB), $-4 /* dcinval(virtaddr, count) */
+ SYNC
+ MOVW n+4(FP), R4
+ RLWNM $0, R3, $~(CACHELINESZ-1), R5
+ CMP R4, $0
+ BLE dci1
+ SUB R5, R3
+ ADD R3, R4
+ ADD $(CACHELINESZ-1), R4
+ SRAW $CACHELINELOG, R4
+ MOVW R4, CTR
+dci0: DCBI (R5)
+ ADD $CACHELINESZ, R5
+ BDNZ dci0
+ SYNC
+ ISYNC
+dci1:
+ RETURN
+
+TEXT dccci(SB), $-4
+ SYNC
+ DCCCI(0, 3)
+ ISYNC
+ RETURN
+
+TEXT _tas(SB), $0
+ SYNC
+ MOVW R3, R4
+ MOVW $0xdeaddead,R5
+tas1:
+ DCBF (R4) /* fix for 603x bug */
+ LWAR (R4), R3
+ CMP R3, $0
+ BNE tas0
+ STWCCC R5, (R4)
+ BNE tas1
+tas0:
+ SYNC
+ ISYNC
+ RETURN
+
+TEXT gettbl(SB), $0
+ MFTB(TBRL, 3)
+ RETURN
+
+TEXT gettbu(SB), $0
+ MFTB(TBRU, 3)
+ RETURN
+
+TEXT getpvr(SB), $0
+ MOVW SPR(PVR), R3
+ RETURN
+
+TEXT getcallerpc(SB), $-4
+ MOVW 0(R1), R3
+ RETURN
+
+TEXT getdear(SB), $0
+ MOVW SPR(DEAR), R3
+ RETURN
+
+TEXT getdsisr(SB), $0
+ MOVW SPR(DSISR), R3
+ RETURN
+
+TEXT getmsr(SB), $0
+ MOVW MSR, R3
+ RETURN
+
+TEXT putmsr(SB), $0
+ SYNC
+ MOVW R3, MSR
+ MSRSYNC
+ RETURN
+
+TEXT putevpr(SB), $0
+ MOVW R3, SPR(EVPR)
+ RETURN
+
+TEXT getesr(SB), $0
+ MOVW SPR(ESR), R3
+ RETURN
+
+TEXT putesr(SB), $0
+ MOVW R3, SPR(ESR)
+ RETURN
+
+TEXT getpit(SB), $0
+ MOVW SPR(PIT), R3
+ RETURN
+
+TEXT putpit(SB), $0
+ MOVW R3, SPR(PIT)
+ RETURN
+
+TEXT gettsr(SB), $0
+ MOVW SPR(TSR), R3
+ RETURN
+
+TEXT puttsr(SB), $0
+ MOVW R3, SPR(TSR)
+ RETURN
+
+TEXT puttcr(SB), $0
+ MOVW R3, SPR(TCR)
+ RETURN
+
+TEXT eieio(SB), $0
+ EIEIO
+ RETURN
+
+TEXT gotopc(SB), $0
+ MOVW R3, CTR
+ MOVW LR, R31 /* for trace back */
+ BR (CTR)
+
+TEXT getccr0(SB), $-4
+ MOVW SPR(CCR0), R3
+ RETURN
+
+TEXT dcread(SB), $-4
+ MOVW 4(FP), R4
+ MOVW SPR(CCR0), R5
+ RLWNM $0, R5, $~0xFF, R5
+ OR R4, R5
+ MOVW R5, SPR(CCR0)
+ SYNC
+ ISYNC
+ DCREAD(3, 3)
+ RETURN
+
+TEXT getdcr(SB), $-4
+ MOVW $_getdcr(SB), R5
+ SLW $3, R3
+ ADD R3, R5
+ MOVW R5, CTR
+ BR (CTR)
+
+TEXT putdcr(SB), $-4
+ MOVW $_putdcr(SB), R5
+ SLW $3, R3
+ ADD R3, R5
+ MOVW R5, CTR
+ MOVW 8(R1), R3
+ BR (CTR)
+
+TEXT firmware(SB), $0
+ MOVW $(3<<28), R3
+ MOVW R3, SPR(DBCR0) /* system reset */
+ BR 0(PC)
+
+/*
+ * byte swapping of arrays of long and short;
+ * could possibly be avoided with more changes to drivers
+ */
+TEXT swabl(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ SRAW $2, R5, R5
+ MOVW R5, CTR
+ SUB $4, R4
+ SUB $4, R3
+swabl1:
+ ADD $4, R3
+ MOVWU 4(R4), R7
+ MOVWBR R7, (R3)
+ BDNZ swabl1
+ RETURN
+
+TEXT swabs(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ SRAW $1, R5, R5
+ MOVW R5, CTR
+ SUB $2, R4
+ SUB $2, R3
+swabs1:
+ ADD $2, R3
+ MOVHZU 2(R4), R7
+ MOVHBR R7, (R3)
+ BDNZ swabs1
+ RETURN
+
+TEXT legetl(SB), $0
+ MOVWBR (R3), R3
+ RETURN
+
+TEXT lesetl(SB), $0
+ MOVW v+4(FP), R4
+ MOVWBR R4, (R3)
+ RETURN
+
+TEXT legets(SB), $0
+ MOVHBR (R3), R3
+ RETURN
+
+TEXT lesets(SB), $0
+ MOVW v+4(FP), R4
+ MOVHBR R4, (R3)
+ RETURN
+
+TEXT itlbmiss(SB), $-4
+ BR traps
+
+TEXT dtlbmiss(SB), $-4
+ BR traps
+
+/*
+ * traps force memory mapping off.
+ * this code goes to much effort to restore it;
+ * (a little more effort than needed for the Inferno environment)
+ */
+TEXT trapvec(SB), $-4
+traps:
+ MOVW LR, R0
+
+pagefault:
+
+/*
+ * map data virtually and make space to save
+ */
+ MOVW R0, SPR(SAVEXX) /* vector */
+trapcomm:
+ MOVW R1, SPR(SAVER1)
+ SYNC
+ ISYNC
+ MOVW MSR, R0
+ OR $(MSR_DR|MSR_ME), R0 /* make data space usable */
+ SYNC
+ MOVW R0, MSR
+ MSRSYNC
+ SUB $UREGSPACE, R1
+
+ MOVW SPR(SRR0), R0 /* save SRR0/SRR1 now, since DLTB might be missing stack page */
+ MOVW R0, LR
+ MOVW SPR(SRR1), R0
+ RLWNM $0, R0, $~MSR_WE, R0 /* remove wait state */
+ MOVW R0, 12(R1) /* save status: could take DLTB miss here */
+ MOVW LR, R0
+ MOVW R0, 16(R1) /* old PC */
+ BL saveureg(SB)
+ BL trap(SB)
+ BR restoreureg
+
+/*
+ * critical trap/interrupt
+ */
+TEXT trapcvec(SB), $-4
+ MOVW LR, R0
+ /* for now we'll just restore the state to the conventions that trap expects, since we don't use critical intrs yet */
+ MOVW R0, SPR(SAVEXX)
+ MOVW SPR(SRR2), R0
+ MOVW R0, SPR(SRR0)
+ MOVW SPR(SRR3), R0
+ MOVW R0, SPR(SRR1)
+ BR trapcomm
+
+TEXT intrvec(SB), $-4
+ MOVW LR, R0
+
+/*
+ * map data virtually and make space to save
+ */
+ MOVW R0, SPR(SAVEXX) /* vector */
+ MOVW R1, SPR(SAVER1)
+ SYNC
+ ISYNC
+ MOVW MSR, R0
+ OR $MSR_DR, R0 /* make data space usable */
+ SYNC
+ MOVW R0, MSR
+ MSRSYNC
+ SUB $UREGSPACE, R1
+
+ MFTB(TBRL, 0)
+ MOVW R0, intrtbl(SB)
+
+ MOVW SPR(SRR0), R0
+ MOVW R0, LR
+ MOVW SPR(SRR1), R0
+ RLWNM $0, R0, $~MSR_WE, R0 /* remove wait state */
+ MOVW R0, 12(R1)
+ MOVW LR, R0
+ MOVW R0, 16(R1)
+ BL saveureg(SB)
+
+ MFTB(TBRL, 5)
+ MOVW R5, isavetbl(SB)
+
+ BL intr(SB)
+
+/*
+ * restore state from Ureg and return from trap/interrupt
+ */
+restoreureg:
+ MOVMW 48(R1), R2 /* r2:r31 */
+ /* defer R1 */
+ MOVW 40(R1), R0
+ MOVW R0, SPR(SAVER0)
+ MOVW 36(R1), R0
+ MOVW R0, CTR
+ MOVW 32(R1), R0
+ MOVW R0, XER
+ MOVW 28(R1), R0
+ MOVW R0, CR /* CR */
+ MOVW 24(R1), R0
+ MOVW R0, SPR(SAVELR) /* LR */
+ /* pad, skip */
+ MOVW 16(R1), R0
+ MOVW R0, SPR(SRR0) /* old PC */
+ MOVW 12(R1), R0
+ MOVW R0, SPR(SRR1) /* old MSR */
+ /* cause, skip */
+ MOVW 44(R1), R1 /* old SP */
+ MOVW SPR(SAVELR), R0
+ MOVW R0, LR
+ MOVW SPR(SAVER0), R0
+ RFI
+
+TEXT mul64fract(SB), $0
+ MOVW a0+8(FP), R9
+ MOVW a1+4(FP), R10
+ MOVW b0+16(FP), R4
+ MOVW b1+12(FP), R5
+
+ MULLW R10, R5, R13 /* c2 = lo(a1*b1) */
+
+ MULLW R10, R4, R12 /* c1 = lo(a1*b0) */
+ MULHWU R10, R4, R7 /* hi(a1*b0) */
+ ADD R7, R13 /* c2 += hi(a1*b0) */
+
+ MULLW R9, R5, R6 /* lo(a0*b1) */
+ MULHWU R9, R5, R7 /* hi(a0*b1) */
+ ADDC R6, R12 /* c1 += lo(a0*b1) */
+ ADDE R7, R13 /* c2 += hi(a0*b1) + carry */
+
+ MULHWU R9, R4, R7 /* hi(a0*b0) */
+ ADDC R7, R12 /* c1 += hi(a0*b0) */
+ ADDE R0, R13 /* c2 += carry */
+
+ MOVW R12, 4(R3)
+ MOVW R13, 0(R3)
+ RETURN
+
+GLOBL mach0+0(SB), $MACHSIZE
+GLOBL spltbl+0(SB), $4
+GLOBL intrtbl+0(SB), $4
+GLOBL isavetbl+0(SB), $4
diff --git a/os/cerf405/main.c b/os/cerf405/main.c
new file mode 100644
index 00000000..11e172fc
--- /dev/null
+++ b/os/cerf405/main.c
@@ -0,0 +1,719 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../ip/ip.h"
+#include "version.h"
+
+#define MAXCONF 32
+
+extern ulong kerndate;
+extern int cflag;
+int remotedebug;
+
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+
+char *confname[MAXCONF];
+char *confval[MAXCONF];
+int nconf;
+
+void addconf(char *, char *);
+void eepromscan(void);
+
+static void
+options(void)
+{
+// nconf = archconfval(confname, confval, sizeof(confname));
+}
+
+void
+doc(char *m)
+{
+ USED(m);
+ iprint("%s...\n", m);
+}
+
+void
+idoc(char *m)
+{
+ uartputs(m, strlen(m));
+}
+
+static void
+poolsizeinit(void)
+{
+ ulong nb;
+
+ nb = conf.npage*BY2PG;
+ poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+ poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+ poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+static void
+serialconsole(void)
+{
+ char *p;
+ int port, baud;
+
+ p = getconf("console");
+ if(p == nil)
+ p = "0";
+ if(p != nil && !remotedebug){
+ port = strtol(p, nil, 0);
+ baud = 115200;
+ p = getconf("baud");
+ if(p != nil){
+ baud = strtol(p, nil, 0);
+ if(baud < 9600)
+ baud = 9600;
+ }
+ uartspecial(port, baud, &kbdq, &printq, kbdcr2nl);
+ }
+}
+
+void
+main(void)
+{
+ idoc("machinit...\n");
+ machinit();
+ idoc("options...\n");
+ compiledcr();
+ options();
+// archinit();
+ quotefmtinstall();
+ idoc("confinit...\n");
+ confinit();
+ xinit();
+ poolsizeinit();
+ poolinit();
+ idoc("trapinit...\n");
+ trapinit();
+ mmuinit();
+ ioinit();
+ printinit();
+ uartinstall();
+ serialconsole();
+ pcimapinit();
+ eepromscan();
+ doc("clockinit");
+ clockinit();
+ doc("procinit");
+ procinit();
+ cpuidprint();
+ doc("links");
+ links();
+ doc("chandevreset");
+ chandevreset();
+
+ eve = strdup("inferno");
+
+ print("\nInferno %s\n", VERSION);
+ print("Vita Nuova\n");
+ print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+
+ doc("userinit");
+ userinit();
+ doc("schedinit");
+ schedinit();
+}
+
+//ccdv=1 cbdv=2 opdv=2 epdv=3 mpdv=1 ppdv=2
+
+void
+machinit(void)
+{
+ int n;
+
+ n = m->machno;
+ memset(m, 0, sizeof(Mach));
+ m->machno = n;
+ m->mmask = 1<<m->machno;
+ m->cputype = getpvr()>>16;
+ m->delayloop = 20000; /* initial estimate only; set by clockinit */
+ m->speed = 266; /* initial estimate only; set by archinit */
+ m->cpuhz = 266333333;
+ m->vcohz = 799000000;
+ m->pllhz = 266333333;
+ m->plbhz = 133166666;
+ m->opbhz = 66600000;
+ m->epbhz = 44*MHz;
+ m->pcihz = 66600000;
+ m->clockgen = m->cpuhz; /* it's the internal cpu clock */
+}
+
+void
+init0(void)
+{
+ Osenv *o;
+ int i;
+ char buf[2*KNAMELEN];
+
+ up->nerrlab = 0;
+
+ spllo();
+
+ if(waserror())
+ panic("init0");
+ /*
+ * These are o.k. because rootinit is null.
+ * Then early kproc's will have a root and dot.
+ */
+ o = up->env;
+ o->pgrp->slash = namec("#/", Atodir, 0, 0);
+ cnameclose(o->pgrp->slash->name);
+ o->pgrp->slash->name = newcname("/");
+ o->pgrp->dot = cclone(o->pgrp->slash);
+
+ chandevinit();
+
+ if(!waserror()){
+ ksetenv("cputype", "power", 0);
+ snprint(buf, sizeof(buf), "power %s", conffile);
+ ksetenv("terminal", buf, 0);
+ poperror();
+ }
+ for(i = 0; i < nconf; i++)
+ if(confname[i][0] != '*'){
+ if(!waserror()){
+ ksetenv(confname[i], confval[i], 0);
+ poperror();
+ }
+ }
+
+ poperror();
+ disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+ Proc *p;
+ Osenv *o;
+
+ p = newproc();
+ o = p->env;
+
+ o->fgrp = newfgrp(nil);
+ o->pgrp = newpgrp();
+ o->egrp = newegrp();
+ kstrdup(&o->user, eve);
+
+ strcpy(p->text, "interp");
+
+ /*
+ * Kernel Stack
+ */
+ p->sched.pc = (ulong)init0;
+ p->sched.sp = (ulong)p->kstack+KSTACK;
+
+ ready(p);
+}
+
+Conf conf;
+
+void
+addconf(char *name, char *val)
+{
+ if(nconf >= MAXCONF)
+ return;
+ confname[nconf] = name;
+ confval[nconf] = val;
+ nconf++;
+}
+
+char*
+getconf(char *name)
+{
+ int i;
+
+ for(i = 0; i < nconf; i++)
+ if(cistrcmp(confname[i], name) == 0)
+ return confval[i];
+ return 0;
+}
+
+void
+confinit(void)
+{
+ char *p;
+ int pcnt;
+
+
+ if(p = getconf("*kernelpercent"))
+ pcnt = 100 - strtol(p, 0, 0);
+ else
+ pcnt = 0;
+
+ archconfinit();
+
+ conf.npage = conf.npage0 + conf.npage1;
+ if(pcnt < 10)
+ pcnt = 70;
+ conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG;
+
+ conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+ conf.nmach = MAXMACH;
+
+}
+
+static void
+twinkle(void)
+{
+ if(m->ticks%MS2TK(1000) == 0)
+ ((Gpioregs*)PHYSGPIO)->or ^= 1<<31;
+}
+
+void (*archclocktick)(void) = twinkle;
+
+void
+exit(int ispanic)
+{
+ up = 0;
+ spllo();
+ print("cpu %d exiting\n", m->machno);
+
+ /* Shutdown running devices */
+ chandevshutdown();
+
+ delay(1000);
+ splhi();
+ if(ispanic)
+ for(;;);
+ archreboot();
+}
+
+void
+reboot(void)
+{
+ exit(0);
+}
+
+void
+halt(void)
+{
+ print("cpu halted\n");
+ microdelay(1000);
+ for(;;)
+ ;
+}
+
+/*
+ * kept in case it's needed for PCI/ISA devices
+ */
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+ char cc[KNAMELEN], *p;
+ int i;
+
+ snprint(cc, sizeof cc, "%s%d", class, ctlrno);
+ p = getconf(cc);
+ if(p == nil)
+ return 0;
+
+ 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, "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);
+ else if(cistrncmp(p, "dma=", 4) == 0)
+ isa->dma = strtoul(p+4, &p, 0);
+ }
+ return 1;
+}
+
+/*
+ * Save the mach dependent part of the process state.
+ */
+void
+procsave(Proc*)
+{
+}
+
+void
+idlehands(void)
+{
+ putmsr(getmsr() | MSR_WE | MSR_EE | MSR_CE); /* MSR_DE as well? */
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+ return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+ return 0;
+}
+
+/*
+ * some of this is possibly ice-cube specific
+ */
+
+enum {
+ Cpc0Pllmr0= 0xF0, /* PLL mode register 0 */
+ Cpc0Boot= 0xF1, /* clock status */
+ Cpc0Pllmr1= 0xF4, /* PLL mode register 1 */
+ Cpc0Srr= 0xF6, /* PCI soft reset */
+ Cpc0PCI= 0xF9, /* PCI control */
+};
+/*
+00f0 = 00011101
+00f1 = 00000025
+00f2 = 00000000
+00f3 = 00000000
+00f4 = 8085523e
+00f5 = 00000017
+00f6 = 00000000
+ccdv=1 cbdv=2 opdv=2 epdv=3 mpdv=1 ppdv=2
+fbmul=8 fwdva=5 fwdvb=5 tun=257 m=40
+*/
+void
+archconfinit(void)
+{
+ ulong ktop;
+
+ conf.npage0 = (32*1024*1024)/BY2PG;
+ conf.base0 = 0;
+ ktop = PGROUND((ulong)end);
+ ktop = PADDR(ktop) - conf.base0;
+ conf.npage0 -= ktop/BY2PG;
+ conf.base0 += ktop;
+
+ {int i; for(i=0xF0; i<=0xF6; i++){iprint("%.4ux = %.8lux\n", i, getdcr(i));}}
+ {
+ int ccdv, cbdv, opdv, epdv, mpdv, ppdv;
+ int fbmul, fwdva, fwdvb, tun;
+ ulong mr0, mr1;
+
+ mr0 = getdcr(Cpc0Pllmr0);
+ ccdv = ((mr0>>20)&3)+1;
+ cbdv = ((mr0>>16)&3)+1;
+ opdv = ((mr0>>12)&3)+1;
+ epdv = ((mr0>>8)&3)+2;
+ mpdv = ((mr0>>4)&3)+1;
+ ppdv = (mr0&3)+1;
+ iprint("ccdv=%d cbdv=%d opdv=%d epdv=%d mpdv=%d ppdv=%d\n",
+ ccdv, cbdv, opdv, epdv, mpdv, ppdv);
+ mr1 = getdcr(Cpc0Pllmr1);
+ fbmul = (mr1>>20) & 0xF;
+ if(fbmul == 0)
+ fbmul = 16;
+ fwdva = (mr1>>16) & 7;
+ if(fwdva == 0)
+ fwdva = 8;
+ fwdvb = (mr1>>12) & 7;
+ if(fwdvb == 0)
+ fwdvb = 8;
+ tun = mr0 & 0x3FF;
+ iprint("fbmul=%d fwdva=%d fwdvb=%d tun=%d m=%d\n",
+ fbmul, fwdva, fwdvb, tun, fbmul*fwdva);
+ }
+}
+
+void
+archreboot(void)
+{
+ putevpr(~0);
+ firmware(0);
+ for(;;);
+}
+
+void
+clockcheck(void)
+{
+}
+
+void
+cpuidprint(void)
+{
+ iprint("PowerPC 405EP pvr=%8.8lux\n", getpvr());
+ /* TO DO */
+}
+
+#include "../port/flashif.h"
+
+/*
+ * for 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)
+{
+ switch(bank){
+ case 0:
+ f->type = "AMD29F0x0"; /* not right, but will do for now */
+ f->addr = (void*)PHYSFLASH;
+ f->size = FLASHSIZE;
+ f->width = 2;
+ return 0;
+ case 1:
+ f->type = "nand";
+ f->addr = (void*)PHYSNAND;
+ f->size = 0; /* done by probe */
+ f->width = 1;
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
+
+#include "../port/netif.h"
+#include "etherif.h"
+
+enum {
+ /* EMAC-PHY control, tucked away in CPC0 */
+ Cpc0Epctl= 0xF3, /* EMAC-PHY ctl */
+
+ E0Nf= 1<<31, /* Emac0 noise filter enable */
+ E1Nf= 1<<30, /* Emac1 noise filter enable */
+ E1pr= 1<<7, /* Emac1 packet reject is active high */
+ E0pr= 1<<6, /* Emac 0 packet reject is active high */
+ E1rm= 1<<5, /* enable Emac 1 packet removal */
+ E0rm= 1<<4, /* enable Emac 0 packet removal */
+ E1pci= 1<<1, /* Emac 1 clock source is Tx clock output (loopback) */
+ E0pci= 1<<0, /* Emac 0 clock source is Tx clock output (loopback) */
+};
+
+int
+archether(int ctlno, Ether *ether)
+{
+ char name[KNAMELEN], *p;
+ int s;
+
+ if(ctlno > 1)
+ return -1;
+ ether->type = "EMAC";
+ ether->port = ctlno;
+ if(ctlno != 0)
+ snprint(name, sizeof(name), "eth%daddr", ctlno);
+ else
+ strcpy(name, "ethaddr");
+ p = getconf(name);
+ if(p == 0){
+ iprint("ether%d: no %s in EEPROM env\n", ctlno, name);
+ return -1;
+ }
+ parsemac(ether->ea, p, Eaddrlen);
+ s = splhi();
+ putdcr(Cpc0Epctl, getdcr(Cpc0Epctl) | (ctlno?E1Nf:E0Nf));
+ splx(s);
+ return 1;
+}
+
+enum {
+ /* UART control */
+ Cpc0Ucr= 0xF5, /* UART control register */
+
+ U0Dc= 1<<21, /* UART0 DMA clear enable */
+ U0Dt= 1<<20, /* enable UART0 DMA transmit channel */
+ U0Dr= 1<<19, /* enable UART0 DMA receive channel */
+ U1Dc= 1<<18, /* UART1 DMA clear enable */
+ U1Dt= 1<<17, /* enable UART1 DMA transmit channel */
+ U1Dr= 1<<16, /* enable UART1 DMA receive channel */
+ U1Div_s= 8, /* UART1 serial clock divisor (shift) */
+ U1Stop= 1<<8,
+ U0Div_s= 0, /* UART0 serial clock divisor (shift) */
+ U0Stop= 1<<0,
+ UDiv_m= 0x7F, /* UARTx divisor mask */
+};
+
+static ulong
+findserialclock(int rate, ulong *freq)
+{
+ ulong d, b;
+ ulong serialclock;
+ int actual, e, beste, bestd;
+
+ *freq = 0;
+ if(rate == 0)
+ return 0;
+ d = ((m->pllhz+m->opbhz-1)/m->opbhz)*2; /* double to allow for later rounding */
+ beste = 0;
+ bestd = -1;
+ for(; d<=128; d++){
+ serialclock = (2*m->pllhz)/d;
+ b = ((serialclock+8*rate-1)/(rate*16))>>1;
+ actual = ((serialclock+8*b-1)/(b*16))>>1;
+ e = rate-actual;
+ if(e < 0)
+ e = -e;
+ if(bestd < 0 || e < beste || e == beste && (bestd&1) && (d&1)==0){
+ beste = e;
+ bestd = d;
+ }
+ }
+ if(bestd > 0)
+ *freq = m->pllhz/bestd;
+ return bestd;
+}
+
+/*
+ * return a value for UARTn's baud rate generator, and
+ * set a corresponding divsor in the UARTn clock generator
+ * (between 2 and 128)
+ */
+ulong
+archuartclock(int n, int rate)
+{
+ int d, s;
+ ulong m, freq;
+
+ d = findserialclock(rate, &freq);
+ if(d <= 0)
+ d = U0Stop;
+ m = UDiv_m;
+ if(n){
+ d <<= U1Div_s;
+ m <<= U1Div_s;
+ }
+ s = splhi();
+ putdcr(Cpc0Ucr, (getdcr(Cpc0Ucr) & ~m) | d);
+ splx(s);
+ return freq;
+}
+
+void
+archuartdma(int n, int on)
+{
+ ulong r;
+ int s;
+
+ r = n? (U1Dc|U1Dt|U1Dr): (U0Dc|U0Dt|U0Dr);
+ if(on){
+ s = splhi();
+ putdcr(Cpc0Ucr, getdcr(Cpc0Ucr) | r);
+ splx(s);
+ }else{
+ s = splhi();
+ putdcr(Cpc0Ucr, getdcr(Cpc0Ucr) & ~r);
+ splx(s);
+ }
+}
+
+/*
+ * boot environment in eeprom
+ */
+
+enum {
+ EEpromHdr= 8, /* bytes */
+ Envsize= 0x400,
+};
+
+static I2Cdev eedev;
+static struct {
+ uchar buf[Envsize];
+ int size;
+} bootenv;
+
+static int
+eepromitem(uchar *buf, int lim, ulong *off)
+{
+ int l;
+ uchar b;
+
+ if(i2crecv(&eedev, &b, 1, (*off)++) != 1)
+ return -1;
+ l = b;
+ if(l & 0x80){
+ if(i2crecv(&eedev, &b, 1, (*off)++) != 1)
+ return -1;
+ l = ((l & 0x7F)<<8) | b;
+ }
+ if(buf == nil)
+ return l;
+ if(l > lim)
+ l = lim;
+ return i2crecv(&eedev, buf, l, *off);
+}
+
+void
+eepromscan(void)
+{
+ int n, l;
+ ulong off;
+ uchar buf[2];
+ char *p, *ep, *v;
+
+ eedev.addr = 0x50;
+ eedev.salen = 2;
+ i2csetup(1);
+ n = i2crecv(&eedev, buf, sizeof(buf), 0);
+ if(n <= 0){
+ iprint("eepromscan: %d\n", n);
+ return;
+ }
+ if(buf[0] != 0xEF || buf[1] != 0xBE){
+ iprint("eeprom invalid\n");
+ return;
+ }
+ bootenv.size = 0;
+ for(off = EEpromHdr; off < 16384;){
+ l = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off); /* key */
+ if(l <= 0)
+ break;
+ off += l;
+ if(l == 7 && memcmp(bootenv.buf, "PPCBOOT", 7) == 0){ /* intrinsyc key */
+ bootenv.size = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off);
+ break;
+ }
+ l = eepromitem(nil, 0, &off); /* skip value */
+ if(l < 0)
+ break;
+ off += l+2; /* 2 byte crc */
+ }
+ p = (char*)bootenv.buf+4; /* skip crc */
+ ep = p+bootenv.size;
+ for(; p < ep && *p; p += l){
+ l = strlen(p)+1;
+ v = strchr(p, '=');
+ if(v != nil)
+ *v++ = 0;
+ else
+ v = "";
+ addconf(p, v);
+ if(0)
+ iprint("%q = %q\n", p, v);
+ }
+}
+
+ulong
+logfsnow(void)
+{
+ return rtctime();
+}
diff --git a/os/cerf405/mal.c b/os/cerf405/mal.c
new file mode 100644
index 00000000..8a5018d3
--- /dev/null
+++ b/os/cerf405/mal.c
@@ -0,0 +1,334 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ * on the 405EP the MAL is used only by the Ethernet
+ * but we keep it separate even so
+ */
+
+enum {
+ Nrxchan= 2,
+ Ntxchan= 4,
+ Maxchan = 4
+};
+
+enum {
+ /* device control registers */
+ Cfg= 0x180, /* configuration register */
+ Esr= 0x181, /* error status register */
+ Ier= 0x182, /* interrupt enable register */
+ Txcasr= 0x184, /* transmit channel active set register */
+ Txcarr= 0x185, /* transmit channel active reset register */
+ Txeobisr= 0x186, /* transmit end of buffer interrupt status register */
+ Txdeir= 0x187, /* transmit descriptor error interrupt register */
+ Rxcasr= 0x190, /* receive channel active set register */
+ Rxcarr= 0x191, /* receive channel active reset register */
+ Rxeobisr= 0x192, /* receive channel descriptor error interrupt register */
+ Rxdeir= 0x193, /* receive descriptor error interrupt register */
+};
+
+#define TXCTPR(n) (0x1A0+(n)) /* transmit channel table pointer register */
+#define RXCTPR(n) (0x1C0+(n)) /* receive channel table pointer register */
+#define RCBS(n) (0x1E0+(n)) /* receive channel buffer size register */
+
+enum {
+ /* configuration */
+ CfgSr= 1<<31, /* software reset */
+ CfgPlbp0= 0<<22, /* PLB priority (0=lowest) */
+ CfgPlbp1= 1<<22,
+ CfgPlbp2= 2<<22,
+ CfgPlbp3= 3<<22,
+ CfgGa= 1<<21, /* guarded */
+ CfgOa= 1<<20, /* ordered */
+ CfgPlble= 1<<19, /* lock error */
+ CfgPlbt_f= 0xF<<15, /* latency timer field */
+ CfgPlbt_s= 15, /* latency timer (shift) */
+ CfgPlbb= 1<<14, /* burst enable */
+ CfgOpbbl= 1<<7, /* OPB locked */
+ CfgOepie= 1<<2, /* interrupt on every end of packet */
+ CfgLea= 1<<1, /* locked error active */
+ CfgSd= 1<<0, /* scroll to next packet on early termination */
+
+ /* error status */
+ EsrEvb= 1<<31, /* error valid bit */
+ EsrCid_f= 0x7F<<25, /* field: channel ID causing lock error */
+ EsrDe= 1<<20, /* descriptor error */
+ EsrOne= 1<<19, /* OPB non-fullword error */
+ EsrOte= 1<<18, /* OPB timeout error */
+ EsrOse= 1<<17, /* OPB slave error */
+ EsrPein= 1<<16, /* PLB bus error indication */
+ EsrDei= 1<<4, /* descriptor error interrupt */
+ EsrOnei= 1<<3, /* OPB non-fulword error interrupt */
+ EsrOtei= 1<<2, /* OPB timeout error interrupt */
+ EsrOsei= 1<<1, /* OPB slave error interrupt */
+ EsrPbei= 1<<0, /* OPB bus error interrupt */
+
+};
+
+typedef struct Malmem Malmem;
+struct Malmem {
+ Lock;
+ BD* base;
+ BD* limit;
+ BD* avail;
+};
+
+static Malmem malmem;
+
+static Mal* malchans[2][Maxchan];
+
+static void
+errorintr(Ureg*, void*)
+{
+ ulong esr, rxdeir, txdeir;
+
+ /* mal de tête */
+ esr = getdcr(Esr);
+ txdeir = getdcr(Txdeir);
+ rxdeir = getdcr(Rxdeir);
+ iprint("mal: esr=%8.8lux txdeir=%8.8lux rxdeir=%8.8lux\n", esr, txdeir, rxdeir);
+ putdcr(Rxdeir, rxdeir);
+ putdcr(Txdeir, txdeir);
+ putdcr(Esr, esr);
+}
+
+static void
+scanintr(Ureg *ur, ulong ir, Mal *chans[])
+{
+ Mal *ml;
+ int i;
+
+ for(i=0; ir != 0 && i < Maxchan; i++)
+ if(ir & IBIT(i)){
+ ir &= ~IBIT(i);
+ ml = chans[i];
+ if(ml != nil && ml->interrupt != nil)
+ ml->interrupt(ur, ml->arg);
+ /* unexpected interrupt otherwise */
+ }
+}
+
+static void
+txinterrupt(Ureg *ur, void*)
+{
+ ulong ir;
+
+ ir = getdcr(Txeobisr);
+ putdcr(Txeobisr, ir);
+ scanintr(ur, ir, malchans[1]);
+}
+
+static void
+rxinterrupt(Ureg *ur, void*)
+{
+ ulong ir;
+
+ ir = getdcr(Rxeobisr);
+ putdcr(Rxeobisr, ir);
+ scanintr(ur, ir, malchans[0]);
+}
+
+void
+ioinit(void)
+{
+ int i;
+
+ putdcr(Txcarr, ~0);
+ putdcr(Rxcarr, ~0);
+
+ /* reset */
+ putdcr(Cfg, CfgSr);
+ while(getdcr(Cfg) & CfgSr)
+ ; /* at most one system clock */
+
+ /* clear these out whilst we're at it */
+ for(i=0; i<Nrxchan; i++){
+ putdcr(RCBS(i), 0);
+ putdcr(RXCTPR(i), 0);
+ }
+ for(i=0; i<Ntxchan; i++)
+ putdcr(TXCTPR(i), 0);
+
+ putdcr(Cfg, (0xF<<CfgPlbt_s)|CfgPlbb); /* TO DO: check */
+
+ /* Ier */
+ intrenable(VectorMALSERR, errorintr, nil, BUSUNKNOWN, "malserr");
+ intrenable(VectorMALTXDE, errorintr, nil, BUSUNKNOWN, "maltxde");
+ intrenable(VectorMALRXDE, errorintr, nil, BUSUNKNOWN, "malrxde");
+ intrenable(VectorMALTXEOB, txinterrupt, nil, BUSUNKNOWN, "maltxeob");
+ intrenable(VectorMALRXEOB, rxinterrupt, nil, BUSUNKNOWN, "malrxeob");
+ putdcr(Ier, EsrDei | EsrOnei | EsrOtei | EsrOsei | EsrPbei);
+}
+
+Mal*
+malchannel(int n, int tx, void (*intr)(Ureg*, void*), void *arg)
+{
+ Mal *ml;
+
+ if((ml = malchans[tx][n]) == nil){
+ ml = malloc(sizeof(*m));
+ malchans[tx][n] = ml;
+ }
+ ml->n = n;
+ ml->tx = tx;
+ ml->len = 1;
+ ml->arg = arg;
+ ml->interrupt = intr;
+ return ml;
+}
+
+void
+maltxreset(Mal *ml)
+{
+ putdcr(Txcarr, IBIT(ml->n));
+}
+
+void
+maltxinit(Mal *ml, Ring *r)
+{
+ putdcr(TXCTPR(ml->n), PADDR(r->tdr));
+}
+
+void
+maltxenable(Mal *ml)
+{
+ putdcr(Txcasr, getdcr(Txcasr) | IBIT(ml->n));
+}
+
+void
+malrxreset(Mal *ml)
+{
+ putdcr(Rxcarr, IBIT(ml->n));
+}
+
+void
+malrxinit(Mal *ml, Ring *r, ulong limit)
+{
+ putdcr(RXCTPR(ml->n), PADDR(r->rdr));
+ putdcr(RCBS(ml->n), limit);
+}
+
+void
+malrxenable(Mal *ml)
+{
+ putdcr(Rxcasr, getdcr(Rxcasr) | IBIT(ml->n));
+}
+
+/*
+ * initialise receive and transmit buffer rings
+ * to use both Emacs, or two channels per emac, we'll need
+ * to allocate all rx descriptors at once, and all tx descriptors at once,
+ * in a region where all addresses have the same bits 0-12(!);
+ * see p 20-34. of the MAL chapter.
+ *
+ * the ring entries must be aligned on sizeof(BD) boundaries
+ * rings must be uncached, and buffers must align with cache lines since the cache doesn't snoop
+ *
+ * thus, we initialise it once for all, then hand it out as requested.
+ */
+void
+ioringreserve(int nrx, ulong nrb, int ntx, ulong ntb)
+{
+ ulong nb, nbd;
+
+ lock(&malmem);
+ if(malmem.base == nil){
+ nbd = nrx*nrb + ntx*ntb;
+ nb = mmumapsize(nbd*sizeof(BD));
+ /*
+ * the data sheet says in the description of buffer tables that they must be on a 4k boundary,
+ * but the pointer register descriptions say 8 bytes; it seems to be the latter.
+ */
+ malmem.base = mmucacheinhib(xspanalloc(nb, nb, 1<<19), nb);
+ malmem.limit = malmem.base + nbd;
+ malmem.avail = malmem.base;
+ if((PADDR(malmem.base)&~0x7FFFF) != (PADDR(malmem.base)&~0x7FFFF))
+ print("mal: trouble ahead?\n");
+ }
+ unlock(&malmem);
+ if(malmem.base == nil)
+ panic("ioringreserve");
+}
+
+BD*
+bdalloc(ulong nd)
+{
+ BD *b;
+
+ lock(&malmem);
+ b = malmem.avail;
+ if(b+nd > malmem.limit)
+ b = nil;
+ else
+ malmem.avail = b+nd;
+ unlock(&malmem);
+ return b;
+}
+
+int
+ioringinit(Ring* r, int nrdre, int ntdre)
+{
+ int i;
+
+ /* buffers must align with cache lines since the cache doesn't snoop */
+ r->nrdre = nrdre;
+ if(r->rdr == nil)
+ r->rdr = bdalloc(nrdre);
+ if(r->rxb == nil)
+ r->rxb = malloc(nrdre*sizeof(Block*));
+ if(r->rdr == nil || r->rxb == nil)
+ return -1;
+ for(i = 0; i < nrdre; i++){
+ r->rxb[i] = nil;
+ r->rdr[i].length = 0;
+ r->rdr[i].addr = 0;
+ r->rdr[i].status = BDEmpty|BDInt;
+ }
+ r->rdr[i-1].status |= BDWrap;
+ r->rdrx = 0;
+
+ r->ntdre = ntdre;
+ if(r->tdr == nil)
+ r->tdr = bdalloc(ntdre);
+ if(r->txb == nil)
+ r->txb = malloc(ntdre*sizeof(Block*));
+ if(r->tdr == nil || r->txb == nil)
+ return -1;
+ for(i = 0; i < ntdre; i++){
+ r->txb[i] = nil;
+ r->tdr[i].addr = 0;
+ r->tdr[i].length = 0;
+ r->tdr[i].status = 0;
+ }
+ r->tdr[i-1].status |= BDWrap;
+ r->tdrh = 0;
+ r->tdri = 0;
+ r->ntq = 0;
+ return 0;
+}
+
+void
+dumpmal(void)
+{
+ int i;
+
+ iprint("Cfg=%8.8lux\n", getdcr(Cfg));
+ iprint("Esr=%8.8lux\n", getdcr(Esr));
+ iprint("Ier=%8.8lux\n", getdcr(Ier));
+ iprint("Txcasr=%8.8lux\n", getdcr(Txcasr));
+ iprint("Txcarr=%8.8lux\n", getdcr(Txcarr));
+ iprint("Txeobisr=%8.8lux\n", getdcr(Txeobisr));
+ iprint("Txdeir=%8.8lux\n", getdcr(Txdeir));
+ iprint("Rxcasr=%8.8lux\n", getdcr(Rxcasr));
+ iprint("Rxcarr=%8.8lux\n", getdcr(Rxcarr));
+ iprint("Rxeobisr=%8.8lux\n", getdcr(Rxeobisr));
+ iprint("Rxdeir=%8.8lux\n", getdcr(Rxdeir));
+ for(i=0; i<Nrxchan; i++)
+ iprint("Rxctpr[%d]=%8.8lux Rcbs[%d]=%8.8lux\n", i, getdcr(RXCTPR(i)), i, getdcr(RCBS(i)));
+ for(i=0;i<Ntxchan; i++)
+ iprint("Txctpr[%d]=%8.8lux\n", i, getdcr(TXCTPR(i)));
+}
diff --git a/os/cerf405/mem.h b/os/cerf405/mem.h
new file mode 100644
index 00000000..fa0aa82b
--- /dev/null
+++ b/os/cerf405/mem.h
@@ -0,0 +1,147 @@
+/*
+ * Memory and machine-specific definitions. Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+
+#define BI2BY 8 /* bits per byte */
+#define BI2WD 32 /* bits per word */
+#define BY2WD 4 /* bytes per word */
+#define BY2V 8 /* bytes per double word */
+#define BY2PG 4096 /* bytes per page */
+#define WD2PG (BY2PG/BY2WD) /* words per page */
+#define PGSHIFT 12 /* log(BY2PG) */
+#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1))
+#define PGROUND(s) ROUND(s, BY2PG)
+#define CACHELINELOG 5
+#define CACHELINESZ (1<<CACHELINELOG)
+#define CACHESIZE 16384
+#define CACHEWAYSIZE (CACHESIZE/2) /* 2-way set associative */
+
+#define MAXMACH 1 /* max # cpus system can run */
+#define MACHSIZE BY2PG
+
+/*
+ * Time
+ */
+#define HZ 100 /* clock frequency */
+#define MS2HZ (1000/HZ) /* millisec per clock tick */
+#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */
+#define MS2TK(t) ((t)/MS2HZ) /* milliseconds to ticks */
+#define MHz 1000000
+
+/*
+ * 4xx MSR bits
+ */
+
+#define MSR_WE 0x40000 /* wait state enable */
+#define MSR_CE 0x20000 /* critical interrupt enable */
+#define MSR_EE 0x08000 /* enable external/decrementer interrupts */
+#define MSR_PR 0x04000 /* =1, user mode */
+#define MSR_ME 0x01000 /* enable machine check exceptions */
+#define MSR_DWE 0x00400 /* debug wait enable */
+#define MSR_DE 0x00200 /* debug interrupts enable */
+#define MSR_IR 0x00020 /* enable instruction address translation */
+#define MSR_DR 0x00010 /* enable data address translation */
+
+#define KMSR (MSR_ME)
+#define UMSR (MSR_PR|MSR_DE|MSR_CE|MSR_EE|MSR_IR|MSR_DR)
+
+/*
+ * Magic registers
+ */
+
+#define MACH 30 /* R30 is m-> */
+#define USER 29 /* R29 is up-> */
+
+/*
+ * Fundamental addresses
+ */
+
+#define UREGSIZE ((8+32)*4)
+
+/*
+ * MMU
+ */
+
+/* TLBHI */
+#define TLBEPN(x) ((x) & ~0x3FF)
+#define TLB1K (0<<7)
+#define TLB4K (1<<7)
+#define TLB16K (2<<7)
+#define TLB64K (3<<7)
+#define TLB256K (4<<7)
+#define TLB1MB (5<<7)
+#define TLB4MB (6<<7)
+#define TLB16MB (7<<7)
+#define TLBVALID (1<<6)
+#define TLBLE (1<<5) /* little-endian */
+#define TLBU0 (1<<4) /* user-defined attribute */
+
+/* TLBLO */
+#define TLBRPN(x) ((x) & ~0x3FF)
+#define TLBEX (1<<9) /* execute enable */
+#define TLBWR (1<<8) /* write enable */
+#define TLBZONE(x) ((x)<<4)
+#define TLBW (1<<3) /* write-through */
+#define TLBI (1<<2) /* cache inhibit */
+#define TLBM (1<<1) /* memory coherent */
+#define TLBG (1<<0) /* guarded */
+
+/*
+ * Address spaces
+ */
+
+#define KUSEG 0x00000000
+#define KSEG0 0x20000000
+#define KSEG1 0x60000000 /* uncached alias for KSEG0 */
+#define KSEGM 0xE0000000 /* mask to check which seg */
+
+#define KZERO KSEG0 /* base of kernel address space */
+#define KTZERO (KZERO+0x3000) /* first address in kernel text */
+#define KSTACK 8192 /* Size of kernel stack */
+
+#define OCMZERO 0x40000000 /* on-chip memory (virtual and physical--see p 5-1) */
+
+/*
+ * Exception codes (trap vectors)
+ */
+#define CRESET 0x01
+#define CMCHECK 0x02
+#define CDSI 0x03
+#define CISI 0x04
+#define CEI 0x05
+#define CALIGN 0x06
+#define CPROG 0x07
+/* 0x08 (fpu) not used */
+/* 0x09 (dec) not used */
+#define CSYSCALL 0x0C
+/* 0x0D (trace) not used */
+/* 0x0E (fpa) not used */
+#define CPIT 0x10
+/* FIT is 0x1010 */
+/* WDT is 0x1020 */
+#define CDMISS 0x11
+#define CIMISS 0x12
+#define CDEBUG 0x20
+
+/*
+ * exception syndrome register
+ */
+#define ESR_MCI 0x80000000 /* instruction machine check */
+#define ESR_PIL 0x08000000 /* program interrupt: illegal instruction */
+#define ESR_PPR 0x04000000 /* program interrupt: privileged */
+#define ESR_PTR 0x02000000 /* program intterupt: trap with successful compare */
+#define ESR_DST 0x00800000 /* data storage interrupt: store fault */
+#define ESR_DIZ 0x00400000 /* data/instruction storage interrupt: zone fault */
+#define ESR_U0F 0x00008000 /* data storage interrupt: u0 fault */
+
+#include "physmem.h"
+
+/* cerf-cube specific */
+#define PHYSDRAM 0
+#define PHYSFLASH 0xFFE00000
+#define FLASHSIZE 0x200000
+#define PHYSNAND 0x60000000
diff --git a/os/cerf405/mkfile b/os/cerf405/mkfile
new file mode 100644
index 00000000..f43b9232
--- /dev/null
+++ b/os/cerf405/mkfile
@@ -0,0 +1,104 @@
+SYSTARG=Inferno
+OBJTYPE=power
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=cerf #default configuration
+CONFLIST=cerf
+KZERO=0x20003020
+
+SYSTARG=$OSTARG
+OBJTYPE=power
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed
+#INSTALLDIR=/$OBJTYPE
+
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+OBJ=\
+ l.$O\
+ tlb.$O\
+ nofp.$O\
+ clock.$O\
+ compile.$O\
+ fpi.$O\
+ fpimem.$O\
+ fpipower.$O\
+ gpio.$O\
+ main.$O\
+ mal.$O\
+ mmu.$O\
+ rmap.$O\
+ trap.$O\
+ $CONF.root.$O\
+ $IP\
+ $DEVS\
+ $ETHERS\
+ $LINKS\
+ $VGAS\
+ $PORT\
+ $MISC\
+ $OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+
+HFILES=\
+ mem.h\
+ physmem.h\
+ dat.h\
+ fns.h\
+ io.h\
+
+CFLAGS=-wFV -I. -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp
+KERNDATE=`{$NDATE}
+
+#default:V: i$CONF.sq
+default:V: i${CONF}hd
+
+i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD -o $target -T$KZERO -l -R4 $OBJ $CONF.$O $LIBFILES
+ $KSIZE $target
+
+i$CONF.sq: i$CONF
+ sqz -w i$CONF >$target
+
+out=i${CONF}hd
+
+i${CONF}hd: i$CONF
+ mkppcimage i$CONF $out
+
+install:V: i$CONF # i$CONF.sq
+ cp i$CONF $INSTALLDIR/i$CONF
+ #cp i$CONF.sq $INSTALLDIR/i$CONF.sq
+
+uninstall:V:
+ rm -f $ROOT/$OBJDIR/bin/i$CONF
+ rm -f $ROOT/$OBJDIR/bin/i$CONF.sq
+
+<../port/portmkfile
+
+../init/$INIT.dis: ../init/$INIT.b
+ cd ../init; mk $INIT.dis
+
+clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+faultpower.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+devether.$O $ETHERS: etherif.h ../port/netif.h
+archipe.$O: screen.h archipe.h
+
+#$VGAS: screen.h vga.h
+$IP devip.$O: ../ip/ip.h
+
+devboot.$O: devboot.c
+ $CC $CFLAGS devboot.c
+
+devuart.$O: devuart.c
+ $CC $CFLAGS devuart.c
diff --git a/os/cerf405/mmu.c b/os/cerf405/mmu.c
new file mode 100644
index 00000000..ae0e0be9
--- /dev/null
+++ b/os/cerf405/mmu.c
@@ -0,0 +1,122 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+extern ulong tlbtab[], tlbtabe[];
+static int tlbx; /* index of next free entry in TLB */
+
+enum
+{
+ /* on-chip memory dcr */
+ Isarc= 0x018, /* instruction-side address range compare */
+ Iscntl= 0x019, /* instruction-side control register */
+ Isen= 1<<31, /* enable */
+ Dsarc= 0x01A, /* data-side address range compare register */
+ Dscntl= 0x01B, /* data-side control register */
+ Dsen= 1<<31, /* enable */
+ Dof= 1<<30, /* must be one (p. 5-7) */
+};
+
+void
+mmuinit(void)
+{
+ int i;
+
+ /*
+ * the l.s initial TLB settings do nearly all that is needed initially.
+ * clear invalid entries (just for clarity) and record the address
+ * of the first available
+ */
+ tlbx = -1;
+ for(i = 0; i < 64; i++)
+ if((tlbrehi(i) & TLBVALID) == 0){
+ if(tlbx < 0)
+ tlbx = i;
+ tlbwelo(i, 0);
+ tlbwehi(i, 0);
+ }
+
+ iprint("ccr0=%8.8lux\n", getccr0());
+
+ /*
+ * set OCM mapping, assuming:
+ * caches were invalidated earlier;
+ * and we aren't currently using it
+ * must also set a tlb entry that validates the virtual address but
+ * the translation is not used (see p. 5-2)
+ */
+ putdcr(Isarc, OCMZERO);
+ putdcr(Dsarc, OCMZERO);
+ putdcr(Iscntl, Isen);
+ putdcr(Iscntl, Dsen|Dof);
+ tlbwelo(tlbx, OCMZERO|TLBZONE(0)|TLBWR|TLBEX|TLBI);
+ tlbwehi(tlbx, OCMZERO|TLB4K|TLBVALID);
+ tlbx++;
+}
+
+int
+segflush(void *a, ulong n)
+{
+ /* flush dcache then invalidate icache */
+ dcflush(a, n);
+ icflush(a, n);
+ return 0;
+}
+
+/*
+ * return required size and alignment to map n bytes in a tlb entry
+ */
+ulong
+mmumapsize(ulong n)
+{
+ ulong size;
+ int i;
+
+ size = 1024;
+ for(i = 0; i < 8 && size < n; i++)
+ size <<= 2;
+ return size;
+}
+
+/*
+ * map a physical addresses at pa to va, with the given attributes.
+ * the virtual address must not be mapped already.
+ * if va is nil, map it at pa in virtual space.
+ */
+void*
+kmapphys(void *va, ulong pa, ulong nb, ulong attr, ulong le)
+{
+ int s, i;
+ ulong size;
+
+ if(va == nil)
+ va = (void*)pa; /* simplest is to use a 1-1 map */
+ size = 1024;
+ for(i = 0; i < 8 && size < nb; i++)
+ size <<= 2;
+ if(i >= 8)
+ return nil;
+ s = splhi();
+ tlbwelo(tlbx, pa | TLBZONE(0) | attr);
+ tlbwehi(tlbx, (ulong)va | (i<<7) | TLBVALID | le);
+ tlbx++;
+ splx(s);
+ return va;
+}
+
+/*
+ * return an uncached alias for the memory at a
+ */
+void*
+mmucacheinhib(void *a, ulong nb)
+{
+ ulong p;
+
+ if(a == nil)
+ return nil;
+ dcflush(a, nb);
+ p = PADDR(a);
+ return kmapphys((void*)(KSEG1|p), p, nb, TLBWR | TLBI | TLBG, 0);
+}
diff --git a/os/cerf405/nand.c b/os/cerf405/nand.c
new file mode 100644
index 00000000..5567574c
--- /dev/null
+++ b/os/cerf405/nand.c
@@ -0,0 +1,96 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#include "flashif.h"
+
+/*
+ * Cerf405-specific NAND flash interface
+ */
+
+#define BE(n) (1<<(31-(n))) /* big-endian bit numbering */
+
+enum {
+ /* GPIO lines */
+ Gpio_CLE_o_b= 31,
+ Gpio_ALE_o_b= 30,
+ Gpio_NCE_o_b= 24, /* CE#, active low */
+ Gpio_RDY_i_b= 23,
+
+ /* bit masks */
+ Gpio_CLE_o= BE(Gpio_CLE_o_b),
+ Gpio_ALE_o= BE(Gpio_ALE_o_b),
+ Gpio_NCE_o= BE(Gpio_NCE_o_b),
+ Gpio_RDY_i= BE(Gpio_RDY_i_b),
+
+ Gpio_NAND_o= Gpio_CLE_o | Gpio_ALE_o | Gpio_NCE_o,
+
+ CS_NAND= 1,
+ Gpio_PerCS1_o= BE(10),
+};
+
+void
+archnand_init(Flash*)
+{
+ gpioreserve(Gpio_NAND_o | Gpio_RDY_i);
+ gpioset(Gpio_NAND_o, Gpio_NCE_o);
+ gpioconfig(Gpio_NAND_o, Gpio_out);
+ gpioconfig(Gpio_RDY_i, Gpio_in);
+}
+
+void
+archnand_claim(Flash*, int claim)
+{
+ gpioset(Gpio_NCE_o, claim? 0: Gpio_NCE_o);
+}
+
+void
+archnand_setCLEandALE(Flash*, int cle, int ale)
+{
+ ulong v;
+
+ v = 0;
+ if(cle)
+ v |= Gpio_CLE_o;
+ if(ale)
+ v |= Gpio_ALE_o;
+ gpioset(Gpio_CLE_o | Gpio_ALE_o, v);
+}
+
+/*
+ * could unroll the loops
+ */
+
+void
+archnand_read(Flash *f, void *buf, int len)
+{
+ uchar *p, *bp;
+
+ p = f->addr;
+ if(buf != nil){
+ bp = buf;
+ while(--len >= 0)
+ *bp++ = *p;
+ }else{
+ int junk;
+ while(--len >= 0){
+ junk = *p;
+ USED(junk);
+ }
+ }
+}
+
+void
+archnand_write(Flash *f, void *buf, int len)
+{
+ uchar *p, *bp;
+
+ p = f->addr;
+ bp = buf;
+ while(--len >= 0)
+ *p = *bp++;
+}
diff --git a/os/cerf405/nofp.s b/os/cerf405/nofp.s
new file mode 100644
index 00000000..f23d49b0
--- /dev/null
+++ b/os/cerf405/nofp.s
@@ -0,0 +1,31 @@
+/*
+ * stubs when no floating-point hardware
+ */
+
+TEXT kfpinit(SB), $0
+ RETURN
+
+TEXT getfpscr(SB), $8
+ MOVW $0, R3
+ RETURN
+
+TEXT fpsave(SB), $0
+ RETURN
+
+TEXT fprestore(SB), $0
+ RETURN
+
+TEXT clrfptrap(SB), $0
+ RETURN
+
+TEXT fpinit(SB), $0
+ RETURN
+
+TEXT fpoff(SB), $0
+ RETURN
+
+TEXT FPsave(SB), 1, $0
+ RETURN
+
+TEXT FPrestore(SB), 1, $0
+ RETURN
diff --git a/os/cerf405/pci.c b/os/cerf405/pci.c
new file mode 100644
index 00000000..30908dc0
--- /dev/null
+++ b/os/cerf405/pci.c
@@ -0,0 +1,981 @@
+/*
+ * PCI support code.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define DBG if(0) pcilog
+
+typedef struct Pcicfg Pcicfg;
+struct Pcicfg {
+ ulong addr;
+ union {
+ ulong l;
+ uchar b[4];
+ ushort s[2];
+ } data;
+};
+
+static Pcicfg* pcicfg;
+static ulong* pciack;
+static ulong* pcimem;
+
+struct
+{
+ char output[16384];
+ int ptr;
+}PCICONS;
+
+int
+pcilog(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);
+
+ memmove(PCICONS.output+PCICONS.ptr, buf, n);
+ PCICONS.ptr += n;
+ return n;
+}
+
+enum
+{ /* configuration mechanism #1 */
+ MaxFNO = 7,
+ MaxUBN = 255,
+};
+
+enum
+{ /* command register */
+ IOen = (1<<0),
+ MEMen = (1<<1),
+ MASen = (1<<2),
+ MemWrInv = (1<<4),
+ PErrEn = (1<<6),
+ SErrEn = (1<<8),
+};
+
+static Lock pcicfglock;
+static QLock pcicfginitlock;
+static int pcicfgmode = -1;
+static int pcimaxbno = 7;
+static int pcimaxdno;
+static Pcidev* pciroot;
+static Pcidev* pcilist;
+static Pcidev* pcitail;
+
+static int pcicfgrw32(int, int, int, int);
+static int pcicfgrw8(int, int, int, int);
+
+static char* bustypes[] = {
+[BusOPB] "OPB",
+[BusPLB] "PLB",
+[BusPCI] "PCI",
+};
+
+#pragma varargck type "T" int
+
+static int
+tbdffmt(Fmt* fmt)
+{
+ char *p;
+ int l, r, type, tbdf;
+
+ if((p = malloc(READSTR)) == nil)
+ return fmtstrcpy(fmt, "(tbdfconv)");
+
+ switch(fmt->r){
+ case 'T':
+ tbdf = va_arg(fmt->args, int);
+ type = BUSTYPE(tbdf);
+ if(type < nelem(bustypes))
+ l = snprint(p, READSTR, bustypes[type]);
+ else
+ l = snprint(p, READSTR, "%d", type);
+ snprint(p+l, READSTR-l, ".%d.%d.%d",
+ BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+ break;
+
+ default:
+ snprint(p, READSTR, "(tbdfconv)");
+ break;
+ }
+ r = fmtstrcpy(fmt, p);
+ free(p);
+
+ return r;
+}
+
+ulong
+pcibarsize(Pcidev *p, int rno)
+{
+ ulong v, size;
+
+ v = pcicfgrw32(p->tbdf, rno, 0, 1);
+ pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
+ size = pcicfgrw32(p->tbdf, rno, 0, 1);
+ if(v & 1)
+ size |= 0xFFFF0000;
+ pcicfgrw32(p->tbdf, rno, v, 0);
+
+ return -(size & ~0x0F);
+}
+
+static int
+pcisizcmp(void *a, void *b)
+{
+ Pcisiz *aa, *bb;
+
+ aa = a;
+ bb = b;
+ return aa->siz - bb->siz;
+}
+
+static ulong
+pcimask(ulong v)
+{
+ ulong m;
+
+ m = BI2BY*sizeof(v);
+ for(m = 1<<(m-1); m != 0; m >>= 1) {
+ if(m & v)
+ break;
+ }
+
+ m--;
+ if((v & m) == 0)
+ return v;
+
+ v |= m;
+ return v+1;
+}
+
+static void
+pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
+{
+ Pcidev *p;
+ int ntb, i, size, rno, hole;
+ ulong v, mema, ioa, sioa, smema, base, limit;
+ Pcisiz *table, *tptr, *mtb, *itb;
+ extern void qsort(void*, long, long, int (*)(void*, void*));
+
+ ioa = *pioa;
+ mema = *pmema;
+
+ DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n",
+ wrreg, root->tbdf, mema, ioa);
+
+ ntb = 0;
+ for(p = root; p != nil; p = p->link)
+ ntb++;
+
+ ntb *= (PciCIS-PciBAR0)/4;
+ table = malloc(2*ntb*sizeof(Pcisiz));
+ itb = table;
+ mtb = table+ntb;
+
+ /*
+ * Build a table of sizes
+ */
+ for(p = root; p != nil; p = p->link) {
+ if(p->ccrb == 0x06) {
+ if(p->ccru == 0x04 && p->bridge != nil) {
+ sioa = ioa;
+ smema = mema;
+ pcibusmap(p->bridge, &smema, &sioa, 0);
+
+ hole = pcimask(smema-mema);
+ if(hole < (1<<20))
+ hole = 1<<20;
+ p->mema.size = hole;
+
+ hole = pcimask(sioa-ioa);
+ if(hole < (1<<12))
+ hole = 1<<12;
+
+ p->ioa.size = hole;
+
+ itb->dev = p;
+ itb->bar = -1;
+ itb->siz = p->ioa.size;
+ itb++;
+
+ mtb->dev = p;
+ mtb->bar = -1;
+ mtb->siz = p->mema.size;
+ mtb++;
+ }
+ if((pcicfgr8(p, PciHDT)&0x7f) != 0)
+ continue;
+ }
+
+ for(i = 0; i <= 5; i++) {
+ rno = PciBAR0 + i*4;
+ v = pcicfgrw32(p->tbdf, rno, 0, 1);
+ size = pcibarsize(p, rno);
+ if(size == 0)
+ continue;
+
+ if(v & 1) {
+ itb->dev = p;
+ itb->bar = i;
+ itb->siz = size;
+ itb++;
+ }
+ else {
+ mtb->dev = p;
+ mtb->bar = i;
+ mtb->siz = size;
+ mtb++;
+ }
+
+ p->mem[i].size = size;
+ }
+ }
+
+ /*
+ * Sort both tables IO smallest first, Memory largest
+ */
+ qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
+ tptr = table+ntb;
+ qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
+
+ /*
+ * Allocate IO address space on this bus
+ */
+ for(tptr = table; tptr < itb; tptr++) {
+ hole = tptr->siz;
+ if(tptr->bar == -1)
+ hole = 1<<12;
+ ioa = (ioa+hole-1) & ~(hole-1);
+
+ p = tptr->dev;
+ if(tptr->bar == -1)
+ p->ioa.bar = ioa;
+ else {
+ p->pcr |= IOen;
+ p->mem[tptr->bar].bar = ioa|1;
+ if(wrreg)
+ pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0);
+ }
+
+ ioa += tptr->siz;
+ }
+
+ /*
+ * Allocate Memory address space on this bus
+ */
+ for(tptr = table+ntb; tptr < mtb; tptr++) {
+ hole = tptr->siz;
+ if(tptr->bar == -1)
+ hole = 1<<20;
+ mema = (mema+hole-1) & ~(hole-1);
+
+ p = tptr->dev;
+ if(tptr->bar == -1)
+ p->mema.bar = mema;
+ else {
+ p->pcr |= MEMen;
+ p->mem[tptr->bar].bar = mema;
+ if(wrreg)
+ pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0);
+ }
+ mema += tptr->siz;
+ }
+
+ *pmema = mema;
+ *pioa = ioa;
+ free(table);
+
+ if(wrreg == 0)
+ return;
+
+ /*
+ * Finally set all the bridge addresses & registers
+ */
+ for(p = root; p != nil; p = p->link) {
+ if(p->bridge == nil) {
+ pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+ p->pcr |= MASen;
+ pcicfgrw32(p->tbdf, PciPCR, p->pcr, 0);
+ continue;
+ }
+
+ base = p->ioa.bar;
+ limit = base+p->ioa.size-1;
+ v = pcicfgrw32(p->tbdf, PciBAR3, 0, 1);
+ v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8);
+ pcicfgrw32(p->tbdf, PciBAR3, v, 0);
+ v = (limit & 0xFFFF0000)|(base>>16);
+ pcicfgrw32(p->tbdf, 0x30, v, 0);
+
+ base = p->mema.bar;
+ limit = base+p->mema.size-1;
+ v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16);
+ pcicfgrw32(p->tbdf, PciBAR4, v, 0);
+
+ /*
+ * Disable memory prefetch
+ */
+ pcicfgrw32(p->tbdf, PciBAR5, 0x0000FFFF, 0);
+ pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+ /*
+ * Enable the bridge
+ */
+ v = 0xFFFF0000 | IOen | MEMen | MASen;
+ pcicfgrw32(p->tbdf, PciPCR, v, 0);
+
+ sioa = p->ioa.bar;
+ smema = p->mema.bar;
+ pcibusmap(p->bridge, &smema, &sioa, 1);
+ }
+}
+
+static int
+pcilscan(int bno, Pcidev** list)
+{
+ Pcidev *p, *head, *tail;
+ int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
+
+ maxubn = bno;
+ head = nil;
+ tail = nil;
+ for(dno = 0; dno <= pcimaxdno; dno++){
+ maxfno = 0;
+ for(fno = 0; fno <= maxfno; fno++){
+ /*
+ * For this possible device, form the
+ * bus+device+function triplet needed to address it
+ * and try to read the vendor and device ID.
+ * If successful, allocate a device struct and
+ * start to fill it in with some useful information
+ * from the device's configuration space.
+ */
+ tbdf = MKBUS(BusPCI, bno, dno, fno);
+ l = pcicfgrw32(tbdf, PciVID, 0, 1);
+ if(l == 0xFFFFFFFF || l == 0)
+ continue;
+ p = malloc(sizeof(*p));
+ p->tbdf = tbdf;
+ p->vid = l;
+ p->did = l>>16;
+
+ if(pcilist != nil)
+ pcitail->list = p;
+ else
+ pcilist = p;
+ pcitail = p;
+
+ p->rid = pcicfgr8(p, PciRID);
+ p->ccrp = pcicfgr8(p, PciCCRp);
+ p->ccru = pcicfgr8(p, PciCCRu);
+ p->ccrb = pcicfgr8(p, PciCCRb);
+ p->pcr = pcicfgr32(p, PciPCR);
+
+ p->intl = pcicfgr8(p, PciINTL);
+
+ /*
+ * If the device is a multi-function device adjust the
+ * loop count so all possible functions are checked.
+ */
+ hdt = pcicfgr8(p, PciHDT);
+ if(hdt & 0x80)
+ maxfno = MaxFNO;
+
+ /*
+ * If appropriate, read the base address registers
+ * and work out the sizes.
+ */
+ switch(p->ccrb) {
+ case 0x01: /* mass storage controller */
+ case 0x02: /* network controller */
+ case 0x03: /* display controller */
+ case 0x04: /* multimedia device */
+ case 0x06: /* bridge device */
+ case 0x07: /* simple comm. controllers */
+ case 0x08: /* base system peripherals */
+ case 0x09: /* input devices */
+ case 0x0A: /* docking stations */
+ case 0x0B: /* processors */
+ case 0x0C: /* serial bus controllers */
+ if((hdt & 0x7F) != 0)
+ break;
+ rno = PciBAR0 - 4;
+ for(i = 0; i < nelem(p->mem); i++) {
+ rno += 4;
+ p->mem[i].bar = pcicfgr32(p, rno);
+ p->mem[i].size = pcibarsize(p, rno);
+ }
+ break;
+
+ case 0x00:
+ case 0x05: /* memory controller */
+ default:
+ break;
+ }
+
+ if(head != nil)
+ tail->link = p;
+ else
+ head = p;
+ tail = p;
+ }
+ }
+
+ *list = head;
+ for(p = head; p != nil; p = p->link){
+ /*
+ * Find PCI-PCI bridges and recursively descend the tree.
+ */
+ if(p->ccrb != 0x06 || p->ccru != 0x04)
+ continue;
+
+ /*
+ * If the secondary or subordinate bus number is not
+ * initialised try to do what the PCI BIOS should have
+ * done and fill in the numbers as the tree is descended.
+ * On the way down the subordinate bus number is set to
+ * the maximum as it's not known how many buses are behind
+ * this one; the final value is set on the way back up.
+ */
+ sbn = pcicfgr8(p, PciSBN);
+ ubn = pcicfgr8(p, PciUBN);
+
+ if(sbn == 0 || ubn == 0) {
+ sbn = maxubn+1;
+ /*
+ * Make sure memory, I/O and master enables are
+ * off, set the primary, secondary and subordinate
+ * bus numbers and clear the secondary status before
+ * attempting to scan the secondary bus.
+ *
+ * Initialisation of the bridge should be done here.
+ */
+ pcicfgw32(p, PciPCR, 0xFFFF0000);
+ l = (MaxUBN<<16)|(sbn<<8)|bno;
+ pcicfgw32(p, PciPBN, l);
+ pcicfgw16(p, PciSPSR, 0xFFFF);
+ maxubn = pcilscan(sbn, &p->bridge);
+ l = (maxubn<<16)|(sbn<<8)|bno;
+
+ pcicfgw32(p, PciPBN, l);
+ }
+ else {
+ maxubn = ubn;
+ pcilscan(sbn, &p->bridge);
+ }
+ }
+
+ return maxubn;
+}
+
+int
+pciscan(int bno, Pcidev **list)
+{
+ int ubn;
+
+ qlock(&pcicfginitlock);
+ ubn = pcilscan(bno, list);
+ qunlock(&pcicfginitlock);
+ return ubn;
+}
+
+static void
+pcicfginit(void)
+{
+ char *p;
+ int bno;
+ Pcidev **list;
+ ulong mema, ioa;
+
+ qlock(&pcicfginitlock);
+ if(pcicfgmode != -1)
+ goto out;
+
+ //pcimmap();
+
+ pcicfgmode = 1;
+ pcimaxdno = 31;
+
+// fmtinstall('T', tbdffmt);
+
+ if(p = getconf("*pcimaxbno"))
+ pcimaxbno = strtoul(p, 0, 0);
+ if(p = getconf("*pcimaxdno"))
+ pcimaxdno = strtoul(p, 0, 0);
+
+
+ list = &pciroot;
+ for(bno = 0; bno <= pcimaxbno; bno++) {
+ int sbno = bno;
+ bno = pcilscan(bno, list);
+
+ while(*list)
+ list = &(*list)->link;
+
+ if (sbno == 0) {
+ Pcidev *pci;
+
+ /*
+ * If we have found a PCI-to-Cardbus bridge, make sure
+ * it has no valid mappings anymore.
+ */
+ pci = pciroot;
+ while (pci) {
+ if (pci->ccrb == 6 && pci->ccru == 7) {
+ ushort bcr;
+
+ /* reset the cardbus */
+ bcr = pcicfgr16(pci, PciBCR);
+ pcicfgw16(pci, PciBCR, 0x40 | bcr);
+ delay(50);
+ }
+ pci = pci->link;
+ }
+ }
+ }
+
+ if(pciroot == nil)
+ goto out;
+
+ /*
+ * Work out how big the top bus is
+ */
+ mema = 0;
+ ioa = 0;
+ pcibusmap(pciroot, &mema, &ioa, 0);
+
+ DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n",
+ mema, pcimask(mema), ioa);
+
+ /*
+ * Align the windows and map it
+ */
+ ioa = 0x1000;
+ mema = 0;
+
+ pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
+
+ pcibusmap(pciroot, &mema, &ioa, 1);
+ DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
+
+out:
+ qunlock(&pcicfginitlock);
+}
+
+static int
+pcicfgrw8(int tbdf, int rno, int data, int read)
+{
+ int o, x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ o = rno & 0x03;
+ rno &= ~0x03;
+ pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+ eieio();
+ if(read)
+ x = pcicfg->data.b[o]; /* TO DO: perhaps o^3 */
+ else
+ pcicfg->data.b[o] = data;
+ eieio();
+ pcicfg->addr = 0;
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr8(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw8(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw8(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw16(int tbdf, int rno, int data, int read)
+{
+ int o, x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ o = (rno >> 1) & 1;
+ rno &= ~0x03;
+ pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+ eieio();
+ if(read)
+ x = pcicfg->data.s[o];
+ else
+ pcicfg->data.s[o] = data;
+ eieio();
+ pcicfg->addr = 0;
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr16(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw16(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw16(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw32(int tbdf, int rno, int data, int read)
+{
+ int x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ rno &= ~0x03;
+ pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+ eieio();
+ if(read)
+ x = pcicfg->data.l;
+ else
+ pcicfg->data.l = data;
+ eieio();
+ pcicfg->addr = 0;
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr32(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw32(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw32(pcidev->tbdf, rno, data, 0);
+}
+
+Pcidev*
+pcimatch(Pcidev* prev, int vid, int did)
+{
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ if(prev == nil)
+ prev = pcilist;
+ else
+ prev = prev->list;
+
+ while(prev != nil){
+ if((vid == 0 || prev->vid == vid)
+ && (did == 0 || prev->did == did))
+ break;
+ prev = prev->list;
+ }
+ return prev;
+}
+
+Pcidev*
+pcimatchtbdf(int tbdf)
+{
+ Pcidev *pcidev;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
+ if(pcidev->tbdf == tbdf)
+ break;
+ }
+ return pcidev;
+}
+
+uchar
+pciipin(Pcidev *pci, uchar pin)
+{
+ if (pci == nil)
+ pci = pcilist;
+
+ while (pci) {
+ uchar intl;
+
+ if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
+ return pci->intl;
+
+ if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
+ return intl;
+
+ pci = pci->list;
+ }
+ return 0;
+}
+
+static void
+pcilhinv(Pcidev* p)
+{
+ int i;
+ Pcidev *t;
+
+ if(p == nil) {
+ putstrn(PCICONS.output, PCICONS.ptr);
+ p = pciroot;
+ print("bus dev type vid did intl memory\n");
+ }
+ for(t = p; t != nil; t = t->link) {
+ print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ",
+ BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
+ t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
+
+ for(i = 0; i < nelem(p->mem); i++) {
+ if(t->mem[i].size == 0)
+ continue;
+ print("%d:%.8lux %d ", i,
+ t->mem[i].bar, t->mem[i].size);
+ }
+ if(t->ioa.bar || t->ioa.size)
+ print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size);
+ if(t->mema.bar || t->mema.size)
+ print("mema:%.8lux %d ", t->mema.bar, t->mema.size);
+ if(t->bridge)
+ print("->%d", BUSBNO(t->bridge->tbdf));
+ print("\n");
+ }
+ while(p != nil) {
+ if(p->bridge != nil)
+ pcilhinv(p->bridge);
+ p = p->link;
+ }
+}
+
+void
+pcihinv(Pcidev* p)
+{
+ if(pcicfgmode == -1)
+ pcicfginit();
+ qlock(&pcicfginitlock);
+ pcilhinv(p);
+ qunlock(&pcicfginitlock);
+}
+
+void
+pcishutdown(void)
+{
+ Pcidev *p;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ for(p = pcilist; p != nil; p = p->list){
+ /* don't mess with the bridges */
+ if(p->ccrb == 0x06)
+ continue;
+ pciclrbme(p);
+ }
+}
+
+void
+pcisetbme(Pcidev* p)
+{
+ int pcr;
+
+ pcr = pcicfgr16(p, PciPCR);
+ pcr |= MASen;
+ pcicfgw16(p, PciPCR, pcr);
+}
+
+void
+pciclrbme(Pcidev* p)
+{
+ int pcr;
+
+ pcr = pcicfgr16(p, PciPCR);
+ pcr &= ~MASen;
+ pcicfgw16(p, PciPCR, pcr);
+}
+
+/*
+ * 405EP specific
+ */
+
+typedef struct Pciplbregs Pciplbregs;
+struct Pciplbregs {
+ struct {
+ ulong la;
+ ulong ma;
+ ulong pcila;
+ ulong pciha;
+ } pmm[3];
+ struct {
+ ulong ms;
+ ulong la;
+ } ptm[2];
+};
+
+enum { /* mask/attribute registers */
+ Pre= 1<<1, /* enable prefetching (PMM only) */
+ Ena= 1<<0, /* enable PLB to PCI map (PMM); enable PCI to PLB (PTM) */
+};
+
+enum { /* DCR */
+ Cpc0Srr= 0xF6, /* PCI soft reset */
+ Rpci= 1<<18, /* reset PCI bridge */
+ Cpc0PCI= 0xF9, /* PCI control */
+ Spe= 1<<4, /* PCIINT/WE select */
+ Hostcfgen= 1<<3, /* enable host config */
+ Arben= 1<<1, /* enable internal arbiter */
+};
+
+enum {
+ /* PciPCR */
+ Se= 1<<8, /* enable PCISErr# when parity error detected as target */
+ Per= 1<<6, /* enable PERR# on parity errors */
+ Me= 1<<2, /* enable bridge-to-master cycles */
+ Ma= 1<<1, /* enable memory access (when PCI memory target) */
+
+ /* PciPSR */
+ Depe= 1<<15, /* parity error */
+ Sse= 1<<14, /* signalled system error */
+ Rma= 1<<13, /* received master abort */
+ Rta= 1<<12, /* received target abort */
+ Sta= 1<<11, /* signalled target abort */
+ Dpe= 1<<8, /* data parity error */
+ F66C= 1<<5, /* 66MHz capable */
+
+ /* PciBARn */
+ Pf= 1<<3, /* prefetchable */
+};
+
+/*
+pmm 0: la=00000080 ma=010000c0 pcila=00000080 pciha=00000000
+pmm 1: la=00000000 ma=00000000 pcila=00000000 pciha=00000000
+pmm 2: la=00000000 ma=00000000 pcila=00000000 pciha=00000000
+ptm 0: ms=01000080 la=00000000
+ptm 1: ms=00000000 la=00000000
+*/
+
+static void
+pcidumpdev(ulong tbdf)
+{
+ int i;
+
+ for(i=0; i<0x68; i+=4)
+ iprint("[%.2x]=%.8ux\n", i, pcicfgrw32(tbdf, i, 0, 1));
+}
+
+void
+pcimapinit(void)
+{
+ Pciplbregs *pm;
+ int i, bridge, psr;
+
+ pm = kmapphys(nil, PHYSPCIBCFG, sizeof(Pciplbregs), TLBWR | TLBI | TLBG, TLBLE);
+ if(0){
+ /* see what the bootstrap left */
+ iprint("PCI:\n");
+ for(i=0; i<3; i++)
+ iprint("pmm %d: la=%.8lux ma=%.8lux pcila=%.8lux pciha=%.8lux\n",
+ i, pm->pmm[i].la, pm->pmm[i].ma, pm->pmm[i].pcila, pm->pmm[i].pciha);
+ for(i=0; i<2; i++)
+ iprint("ptm %d: ms=%.8lux la=%.8lux\n",
+ i, pm->ptm[i].ms, pm->ptm[i].la);
+ }
+ putdcr(Cpc0Srr, Rpci);
+ delay(1);
+ putdcr(Cpc0Srr, 0);
+
+ kmapphys((void*)PHYSPCIIO0, PHYSPCIIO0, 64*1024, TLBWR | TLBI | TLBG, TLBLE);
+ pcicfg = kmapphys(nil, PHYSPCIADDR, sizeof(Pcicfg), TLBWR | TLBI | TLBG, TLBLE);
+ pciack = kmapphys(nil, PHYSPCIACK, sizeof(ulong), TLBWR | TLBI | TLBG, TLBLE);
+ eieio();
+
+ /*
+ * PLB addresses between PHYSPCIBRIDGE and PHYSPCIBRIDGE+64Mb
+ * are mapped to PCI memory at 0.
+ */
+ pm->pmm[0].ma = 0; /* disable during update */
+ eieio();
+ pm->pmm[0].la = PHYSPCIBRIDGE;
+ pm->pmm[0].pcila = 0;
+ pm->pmm[0].pciha = 0;
+ eieio();
+ pm->pmm[0].ma = 0xFC000000 | Ena; /* enable prefetch? */
+ for(i=1; i<3; i++)
+ pm->pmm[i].ma = 0; /* disable the others */
+ eieio();
+ pcimem = kmapphys((void*)PHYSPCIBRIDGE, PHYSPCIBRIDGE, 0x4000000, TLBWR | TLBI | TLBG, TLBLE);
+
+ /*
+ * addresses presented by a PCI device between PCIWINDOW and PCIWINDOW+1Gb
+ * are mapped to physical memory.
+ */
+ pm->ptm[0].ms = 0;
+ eieio();
+ pm->ptm[0].la = PCIWINDOW;
+ eieio();
+ pm->ptm[0].ms = 0xC0000000 | Ena; /* always enabled by hardware */
+ pm->ptm[1].ms = 0;
+ eieio();
+
+ iprint("cpc0pci=%.8lux\n", getdcr(Cpc0PCI));
+
+ /*
+ * the 405ep's pci bridge contains IBM vendor & devid, but
+ * ppcboot rather annoyingly clears them; put them back.
+ */
+ pcicfgmode = 1;
+ pcimaxdno = 31;
+
+ bridge = MKBUS(BusPCI, 0, 0, 0);
+ pcicfgrw16(bridge, PciPCR, Me | Ma, 0);
+ pcicfgrw16(bridge, PciVID, 0x1014, 0);
+ pcicfgrw16(bridge, PciDID, 0x0156, 0);
+ pcicfgrw8(bridge, PciCCRb, 0x06, 0); /* bridge */
+ pcicfgrw32(bridge, PciBAR1, Pf, 0);
+ psr = pcicfgrw16(bridge, PciPSR, 0, 1);
+ if(m->pcihz >= 66000000)
+ psr |= F66C; /* 66 MHz */
+ pcicfgrw16(bridge, PciPSR, psr, 0); /* reset error status */
+ if(0){
+ iprint("pci bridge':\n");
+ pcidumpdev(bridge);
+ }
+
+ pcicfgmode = -1;
+}
diff --git a/os/cerf405/physmem.h b/os/cerf405/physmem.h
new file mode 100644
index 00000000..9fde7920
--- /dev/null
+++ b/os/cerf405/physmem.h
@@ -0,0 +1,22 @@
+/*
+ * Memory-mapped IO
+ */
+
+#define PHYSPCIBRIDGE 0x80000000
+#define PHYSMMIO 0xEF600000
+#define MMIO(i) (PHYSMMIO+(i)*0x100)
+#define PHYSGPT MMIO(0)
+#define PHYSUART0 MMIO(3)
+#define PHYSUART1 MMIO(4)
+#define PHYSIIC MMIO(5)
+#define PHYSOPB MMIO(6)
+#define PHYSGPIO MMIO(7)
+#define PHYSEMAC0 MMIO(8)
+#define PHYSEMAC1 MMIO(9)
+
+#define PHYSPCIIO0 0xE8000000 /* for 64M */
+#define PHYSPCIMEM 0x80000000
+#define PHYSPCIADDR 0xEEC00000 /* for 8 bytes */
+#define PHYSPCIDATA 0xEEC00004
+#define PHYSPCIACK 0xEED00000 /* interrupt acknowledge */
+#define PHYSPCIBCFG 0xEF400000 /* bridge configuration registers */
diff --git a/os/cerf405/powerbreak.c b/os/cerf405/powerbreak.c
new file mode 100644
index 00000000..fc5970ef
--- /dev/null
+++ b/os/cerf405/powerbreak.c
@@ -0,0 +1,123 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "ureg.h"
+
+extern int (*breakhandler)(Ureg *ur, Proc*); /* trap.c */
+extern Instr BREAK; /* trap.c */
+extern void portbreakinit(void);
+
+#define getop(i) ((i>>26)&0x3F)
+#define getxo(i) ((i>>1)&0x3FF)
+#define getbobi(i) bo = (i>>21)&0x1f; bi = (i>>16)&0x1f; xx = (i>>11)&0x1f;
+
+void
+machbreakinit(void)
+{
+ portbreakinit();
+ breakhandler = breakhit;
+}
+
+Instr
+machinstr(ulong addr)
+{
+ if (addr < KTZERO)
+ error(Ebadarg);
+ return *(Instr*)addr;
+}
+
+void
+machbreakset(ulong addr)
+{
+ if (addr < KTZERO)
+ error(Ebadarg);
+ *(Instr*)addr = BREAK;
+ segflush((void*)addr, sizeof(Instr));
+}
+
+void
+machbreakclear(ulong addr, Instr i)
+{
+ if (addr < KTZERO)
+ error(Ebadarg);
+ *(Instr*)addr = i;
+ segflush((void*)addr, sizeof(Instr));
+}
+
+static int
+condok(Ureg *ur, ulong ir, int ctrok)
+{
+ int bo, bi, xx;
+ ulong ctrval;
+
+ ctrval = ur->ctr;
+ getbobi(ir);
+ if(xx)
+ return 0; /* illegal */
+ if((bo & 0x4) == 0) {
+ if(!ctrok)
+ return 0; /* illegal */
+ ctrval--;
+ }
+ if(bo & 0x4 || (ctrval!=0)^((bo>>1)&1)) {
+ if(bo & 0x10 || (((ur->cr & (1L<<(31-bi))!=0)==((bo>>3)&1))))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Return the address of the instruction that will be executed after the
+ * instruction at ur->pc, accounting for current branch conditions.
+ */
+ulong
+machnextaddr(Ureg *ur)
+{
+ long imm;
+ ulong ir;
+
+ ir = *(ulong*)ur->pc;
+ switch(getop(ir)) {
+ case 18: /* branch */
+ imm = ir & 0x03FFFFFC;
+ if(ir & 0x02000000)
+ imm |= 0xFC000000; /* sign extended */
+ if((ir & 2) == 0) /* relative address */
+ return ur->pc + imm;
+ return imm;
+
+ case 16: /* conditional branch */
+ if(condok(ur, ir&0xFFFF0000, 1)){
+ imm = ir & 0xFFFC;
+ if(ir & 0x08000)
+ imm |= 0xFFFF0000; /* sign extended */
+ if((ir & 2) == 0) /* relative address */
+ return ur->pc + imm;
+ return imm;
+ }
+ break;
+
+ case 19: /* conditional branch to register */
+ switch(getxo(ir)){
+ case 528: /* bcctr */
+ if(condok(ur, ir, 0))
+ return ur->ctr & ~3;
+ break;
+ case 16: /* bclr */
+ if(condok(ur, ir, 1))
+ return ur->lr & ~3;
+ break;
+ }
+ break;
+ }
+ return ur->pc+4; /* next instruction */
+}
+
+int
+isvalid_va(void *v)
+{
+ return (ulong)v >= KTZERO;
+}
diff --git a/os/cerf405/rmap.c b/os/cerf405/rmap.c
new file mode 100644
index 00000000..f521c24d
--- /dev/null
+++ b/os/cerf405/rmap.c
@@ -0,0 +1,106 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+void
+mapinit(RMap *rmap, Map *map, int size)
+{
+ lock(rmap);
+ rmap->map = map;
+ rmap->mapend = map+(size/sizeof(Map));
+ unlock(rmap);
+}
+
+void
+mapfree(RMap* rmap, ulong addr, int size)
+{
+ Map *mp;
+ ulong t;
+
+ if(size <= 0)
+ return;
+
+ lock(rmap);
+ for(mp = rmap->map; mp->addr <= addr && mp->size; mp++)
+ ;
+
+ if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){
+ (mp-1)->size += size;
+ if(addr+size == mp->addr){
+ (mp-1)->size += mp->size;
+ while(mp->size){
+ mp++;
+ (mp-1)->addr = mp->addr;
+ (mp-1)->size = mp->size;
+ }
+ }
+ }
+ else{
+ if(addr+size == mp->addr && mp->size){
+ mp->addr -= size;
+ mp->size += size;
+ }
+ else do{
+ if(mp >= rmap->mapend){
+ print("mapfree: %s: losing 0x%luX, %d\n",
+ rmap->name, addr, size);
+ break;
+ }
+ t = mp->addr;
+ mp->addr = addr;
+ addr = t;
+ t = mp->size;
+ mp->size = size;
+ mp++;
+ }while(size = t);
+ }
+ unlock(rmap);
+}
+
+ulong
+rmapalloc(RMap* rmap, ulong addr, int size, int align)
+{
+ Map *mp;
+ ulong maddr, oaddr;
+
+ lock(rmap);
+ for(mp = rmap->map; mp->size; mp++){
+ maddr = mp->addr;
+
+ if(addr){
+ if(maddr > addr)
+ break;
+ if(maddr+mp->size < addr)
+ continue;
+ if(addr+size > maddr+mp->size)
+ break;
+ maddr = addr;
+ }
+
+ if(align > 0)
+ maddr = ((maddr+align-1)/align)*align;
+ if(mp->addr+mp->size-maddr < size)
+ continue;
+
+ oaddr = mp->addr;
+ mp->addr = maddr+size;
+ mp->size -= maddr-oaddr+size;
+ if(mp->size == 0){
+ do{
+ mp++;
+ (mp-1)->addr = mp->addr;
+ }while((mp-1)->size = mp->size);
+ }
+
+ unlock(rmap);
+ if(oaddr != maddr)
+ mapfree(rmap, oaddr, maddr-oaddr);
+
+ return maddr;
+ }
+ unlock(rmap);
+
+ return 0;
+}
diff --git a/os/cerf405/tlb.s b/os/cerf405/tlb.s
new file mode 100644
index 00000000..98abd3fd
--- /dev/null
+++ b/os/cerf405/tlb.s
@@ -0,0 +1,30 @@
+#include "mem.h"
+#define MB (1024*1024)
+
+/*
+ * TLB prototype entries, loaded once-for-all at startup,
+ * remaining unchanged thereafter.
+ * Limit the table size to ensure it fits in small TLBs.
+ */
+#define TLBE(hi, lo) WORD $(hi); WORD $(lo)
+
+TEXT tlbtab(SB), $-4
+
+ /* tlbhi tlblo */
+
+ /* DRAM, 32MB */
+ TLBE(KZERO|PHYSDRAM|TLB16MB|TLBVALID, PHYSDRAM|TLBZONE(0)|TLBWR|TLBEX)
+ TLBE(KZERO|(PHYSDRAM+16*MB)|TLB16MB|TLBVALID, (PHYSDRAM+16*MB)|TLBZONE(0)|TLBWR|TLBEX)
+
+ /* memory-mapped IO, 4K */
+ TLBE(PHYSMMIO|TLB4K|TLBVALID, PHYSMMIO|TLBZONE(0)|TLBWR|TLBI|TLBG)
+
+ /* NAND flash access (4K?) */
+ TLBE(PHYSNAND|TLB4K|TLBVALID, PHYSNAND|TLBZONE(0)|TLBWR|TLBI|TLBG)
+
+ /* NOR flash, 2MB */
+ TLBE(PHYSFLASH|TLB1MB|TLBVALID, PHYSFLASH|TLBZONE(0)|TLBWR|TLBEX)
+ TLBE((PHYSFLASH+MB)|TLB1MB|TLBVALID, (PHYSFLASH+MB)|TLBZONE(0)|TLBWR|TLBEX)
+
+TEXT tlbtabe(SB), $-4
+ RETURN
diff --git a/os/cerf405/trap.c b/os/cerf405/trap.c
new file mode 100644
index 00000000..846cc097
--- /dev/null
+++ b/os/cerf405/trap.c
@@ -0,0 +1,581 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "io.h"
+#include "../port/error.h"
+
+enum
+{
+ Maxhandler= MaxVector /* max number of interrupt handlers, assuming none shared */
+};
+
+enum {
+ /* UIC registers (p. 10-4) */
+ Usr = 0xC0, /* status */
+ Uer = 0xC2, /* enable */
+ Ucr = 0xC3, /* critical interrupts */
+ Upr = 0xC4, /* priority */
+ Utr = 0xC5, /* trigger */
+ Umsr = 0xC6, /* masked status */
+ Uvr = 0xC7, /* vector */
+ Uvcr = 0xC8, /* vector configuration */
+};
+
+typedef struct Handler Handler;
+struct Handler
+{
+ void (*r)(Ureg*, void*);
+ void *arg;
+ char name[KNAMELEN];
+ Handler *next;
+ int edge;
+ ulong nintr;
+ ulong ticks;
+ int maxtick;
+};
+
+static Lock veclock;
+
+static struct
+{
+ Handler *ivec[MaxVector];
+ Handler h[Maxhandler];
+ int free;
+ Handler* freelist;
+} halloc;
+
+Instr BREAK = 0x7fe00008;
+int (*breakhandler)(Ureg*, Proc*);
+
+void kernfault(Ureg*, int);
+
+char *excname[] =
+{
+ "reserved 0",
+ "system reset",
+ "machine check",
+ "data access",
+ "instruction access",
+ "external interrupt",
+ "alignment",
+ "program exception",
+ "floating-point unavailable",
+ "decrementer",
+ "i/o controller interface error",
+ "reserved B",
+ "system call",
+ "trace trap",
+ "floating point assist",
+ "reserved F",
+ "software emulation",
+ "ITLB miss",
+ "DTLB miss",
+ "ITLB error",
+ "DTLB error",
+ "reserved 15",
+ "reserved 16",
+ "reserved 17",
+ "reserved 18",
+ "reserved 19",
+ "reserved 1A",
+ "reserved 1B",
+ "data breakpoint",
+ "instruction breakpoint",
+ "peripheral breakpoint",
+ "development port",
+ /* the following are made up on a program exception */
+ "floating point exception", /* 20: FPEXC */
+ "illegal instruction", /* 21 */
+ "privileged instruction", /* 22 */
+ "trap", /* 23 */
+ "illegal operation", /* 24 */
+ "breakpoint", /* 25 */
+};
+
+char *fpcause[] =
+{
+ "inexact operation",
+ "division by zero",
+ "underflow",
+ "overflow",
+ "invalid operation",
+};
+char *fpexcname(Ureg*, ulong, char*);
+#define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */
+
+char *regname[]={
+ "CAUSE", "SRR1",
+ "PC", "GOK",
+ "LR", "CR",
+ "XER", "CTR",
+ "R0", "R1",
+ "R2", "R3",
+ "R4", "R5",
+ "R6", "R7",
+ "R8", "R9",
+ "R10", "R11",
+ "R12", "R13",
+ "R14", "R15",
+ "R16", "R17",
+ "R18", "R19",
+ "R20", "R21",
+ "R22", "R23",
+ "R24", "R25",
+ "R26", "R27",
+ "R28", "R29",
+ "R30", "R31",
+};
+
+void
+sethvec(int v, void (*r)(void))
+{
+ ulong *vp, pa, o;
+
+ vp = (ulong*)KADDR(v);
+ vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */
+ vp[1] = 0x7c0802a6; /* MOVW LR, R0 */
+ vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */
+ pa = PADDR(r);
+ o = pa >> 25;
+ if(o != 0 && o != 0x7F){
+ /* a branch too far: running from ROM */
+ vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */
+ vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */
+ vp[5] = 0x7c0803a6; /* MOVW R0, LR */
+ vp[6] = 0x4e800021; /* BL (LR) */
+ }else
+ vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */
+ dcflush(vp, 8*sizeof(ulong));
+}
+
+void
+sethvec2(int v, void (*r)(void))
+{
+ ulong *vp;
+
+ vp = (ulong*)KADDR(v);
+ vp[0] = (18<<26)|((ulong)r&~KSEGM)|2; /* ba */
+ dcflush(vp, sizeof(*vp));
+}
+
+static void
+faultpower(Ureg *ur, ulong addr, int read)
+{
+ char buf[ERRMAX];
+
+ if(up == nil){
+ dumpregs(ur);
+ panic("kernel fault");
+ }
+
+ up->dbgreg = ur; /* For remote ACID */
+ spllo();
+
+ sprint(buf, "trap: fault %s pc=0x%lux addr=0x%lux",
+ read? "read": "write", ur->pc, addr);
+ if(up->type == Interp){
+ if(addr == ~0)
+ disfault(ur, "dereference of nil");
+ disfault(ur, buf);
+ }
+ dumpregs(ur);
+ panic("fault: %s\n", buf);
+}
+
+void
+trap(Ureg *ur)
+{
+ int ecode, s;
+ ulong w, esr;
+ char buf[ERRMAX];
+
+ ecode = ur->cause >> 8;
+ if(ecode < 0 || ecode >= 0x1F)
+ ecode = 0x1F;
+ esr = getesr();
+ putesr(0);
+ switch(ecode){
+ case CPIT:
+ clockintr(ur);
+ preemption(1);
+ break;
+
+ case CMCHECK:
+ if(esr & ESR_MCI){
+ faultpower(ur, ur->pc, 1);
+ break;
+ }
+ /* FALL THROUGH */
+ case CDSI:
+ faultpower(ur, getdear(), !(esr&ESR_DST));
+ break;
+
+ case CDMISS:
+ faultpower(ur, getdear(), 1);
+ break;
+
+ case CISI:
+ case CIMISS:
+ faultpower(ur, ur->pc, 1);
+ break;
+
+ case CPROG:
+ if(esr & ESR_PIL){
+ if(up == nil)
+ goto Default;
+ if((ulong)(ur+1) != ur->r1)
+ panic("fp emu stack");
+ spllo();
+ if(waserror()){
+ if(up->type == Interp)
+ disfault(ur, up->env->errstr);
+ panic("%s", up->env->errstr);
+ }
+ if(fpipower(ur) == 0){
+ splhi();
+ poperror();
+ print("pc=#%lux op=#%8.8lux\n", ur->pc, *(ulong*)ur->pc); /* temporary */
+ goto Default;
+ }
+ poperror();
+ break;
+ }
+ /* TO DO: 4xx variant for the following */
+ if(ur->status & (1<<19)) {
+ ecode = 0x20;
+ w = ur->pc;
+ if(ur->status & (1<<16))
+ w += 4;
+ if(*(ulong*)w == 0x7fe00008){ /* tw 31,0,0 */
+ if(breakhandler){
+ s = (*breakhandler)(ur, up);
+ if(s == BrkSched){
+ if(up){
+ up->preempted = 0;
+ sched();
+ splhi();
+ }
+ }else if(s == BrkNoSched){
+ if(up){
+ up->preempted = 1; /* stop it being preempted until next instruction */
+ up->dbgreg = 0;
+ }
+ }
+ break;
+ }
+ ecode = 0x1D; /* breakpoint */
+ }
+ }
+ if(ur->status & (1<<18))
+ ecode = 0x21;
+ if(ur->status & (1<<17))
+ ecode = 0x22;
+ /* FALL THROUGH */
+
+ Default:
+ default:
+ if(up && up->type == Interp) {
+ spllo();
+ snprint(buf, sizeof buf, "sys: trap: %s pc=0x%lux", excname[ecode], ur->pc);
+ error(buf);
+ break;
+ }
+ print("kernel %s pc=0x%lux\n", excname[ecode], ur->pc);
+ dumpregs(ur);
+ dumpstack();
+ if(m->machno == 0)
+ spllo();
+ exit(1);
+ }
+
+ splhi();
+}
+
+void
+trapinit(void)
+{
+ int i;
+
+ putdcr(Uer, 0); /* none enabled */
+ putdcr(Ucr, 0); /* none are critical by default */
+ putdcr(Upr, ~IBIT(VectorPCISERR)); /* default is active high (except PCISERR) */
+ putdcr(Utr, 0); /* all are level sensitive by default */
+ putdcr(Usr, getdcr(Usr)); /* reset interrupts */
+ putdcr(Uvcr, 0); /* 31 is highest priority */
+ eieio();
+
+ /*
+ * set all exceptions to trap
+ */
+ for(i = 0x0; i < 0x3000; i += 0x100)
+ sethvec(i, trapvec);
+
+ /* on the 405, several traps are critical interrupts with different SRRs */
+ sethvec(0x0100, trapcvec);
+ sethvec(0x0200, trapcvec);
+
+ sethvec(CEI<<8, intrvec);
+ /* TO DO: FIT and WDT */
+ //sethvec2(CIMISS<<8, itlbmiss);
+ //sethvec2(CDMISS<<8, dtlbmiss);
+
+ putevpr(0); /* use our vectors */
+}
+
+void
+intrenable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name)
+{
+ Handler *h;
+ ulong w, f, bit;
+
+ f = v;
+ v &= IRQmask;
+ bit = IBIT(v);
+ if(v < 0 || v >= nelem(halloc.ivec))
+ panic("intrenable(%d)", v);
+ ilock(&veclock);
+ if((h = halloc.freelist) == nil){
+ if(halloc.free >= Maxhandler){
+ iunlock(&veclock);
+ panic("out of interrupt handlers");
+ }
+ h = &halloc.h[halloc.free++];
+ }else
+ halloc.freelist = h->next;
+ h->r = r;
+ h->arg = arg;
+ strncpy(h->name, name, KNAMELEN-1);
+ h->name[KNAMELEN-1] = 0;
+ h->next = halloc.ivec[v];
+ halloc.ivec[v] = h;
+
+ /*
+ * enable corresponding interrupt in UIC
+ */
+ w = getdcr(Ucr);
+ if(f & IRQcritical)
+ putdcr(Ucr, w | bit);
+ else
+ putdcr(Ucr, w & ~bit);
+ if(v >= VectorIRQ){
+ /* (only) these have got choice of polarity, etc. */
+ w = getdcr(Utr);
+ h->edge = (f & IRQedge) != 0;
+ if(h->edge)
+ putdcr(Utr, w | bit);
+ else
+ putdcr(Utr, w & ~bit);
+ w = getdcr(Upr);
+ if(f & IRQactivelow)
+ putdcr(Upr, w | bit);
+ else
+ putdcr(Upr, w & ~bit);
+ }
+ eieio();
+ putdcr(Uer, getdcr(Uer) | bit);
+ eieio();
+ iunlock(&veclock);
+}
+
+static void
+irqdisable(int v)
+{
+ putdcr(Uer, getdcr(Uer) & ~IBIT(v));
+}
+
+void
+intrdisable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name)
+{
+ Handler *h, **hp;
+
+ v &= IRQmask;
+ if(v < 0 || v >= nelem(halloc.ivec))
+ panic("intrdisable(%d)", v);
+ ilock(&veclock);
+ for(hp = &halloc.ivec[v]; (h = *hp) != nil; hp = &h->next)
+ if(h->r == r && h->arg == arg && strcmp(h->name, name) == 0){
+ *hp = h->next;
+ h->next = halloc.freelist;
+ halloc.freelist = h;
+ break;
+ }
+ if(halloc.ivec[v] == nil)
+ irqdisable(v);
+ iunlock(&veclock);
+}
+
+/*
+ * called directly by l.s:/intrvec. on a multiprocessor we'd need to lock veclock.
+ */
+void
+intr(Ureg *ur)
+{
+ ulong msr, b;
+ int v;
+ Handler *h;
+ long t0;
+ Proc *oup;
+
+ oup = up;
+ up = nil; /* no process at interrupt level */
+ while((msr = getdcr(Umsr)) != 0){
+ for(v=0; msr!=0 && v<32; v++){
+ b = IBIT(v);
+ if((msr & b) == 0)
+ continue;
+ msr &= ~b;
+ ur->cause = (CEI<<8) | v;
+ h = halloc.ivec[v];
+ if(h == nil){
+ iprint("unknown interrupt %d pc=0x%lux\n", v, ur->pc);
+ irqdisable(v);
+ continue;
+ }
+
+ /*
+ * call the interrupt handlers
+ */
+ do {
+ if(h->edge)
+ putdcr(Usr, b);
+ h->nintr++;
+ t0 = getpit();
+ (*h->r)(ur, h->arg);
+ t0 -= getpit();
+ h->ticks += t0;
+ if(h->maxtick < t0)
+ h->maxtick = t0;
+ if(!h->edge)
+ putdcr(Usr, b);
+ h = h->next;
+ } while(h != nil);
+ }
+ }
+ up = oup;
+ preemption(0);
+}
+
+int
+intrstats(char *buf, int bsize)
+{
+ Handler *h;
+ int i, n;
+
+ n = 0;
+ for(i=0; i<nelem(halloc.ivec) && n < bsize; i++)
+ if((h = halloc.ivec[i]) != nil && h->nintr)
+ n += snprint(buf+n, bsize-n, "%3d %lud %lud %ud\n", i, h->nintr, h->ticks, h->maxtick);
+ return n;
+}
+
+char*
+fpexcname(Ureg *ur, ulong fpscr, char *buf)
+{
+ int i;
+ char *s;
+ ulong fppc;
+
+ fppc = ur->pc;
+ s = 0;
+ fpscr >>= 3; /* trap enable bits */
+ fpscr &= (fpscr>>22); /* anded with exceptions */
+ for(i=0; i<5; i++)
+ if(fpscr & (1<<i))
+ s = fpcause[i];
+ if(s == 0)
+ return "no floating point exception";
+ sprint(buf, "%s fppc=0x%lux", s, fppc);
+ return buf;
+}
+
+#define KERNPC(x) (KTZERO<(ulong)(x)&&(ulong)(x)<(ulong)etext)
+
+void
+kernfault(Ureg *ur, int code)
+{
+ Label l;
+
+ print("panic: kfault %s dear=0x%lux esr=0x%8.8lux\n", excname[code], getdear(), getesr());
+ print("u=0x%lux status=0x%lux pc=0x%lux sp=0x%lux\n",
+ up, ur->status, ur->pc, ur->sp);
+ dumpregs(ur);
+ l.sp = ur->sp;
+ l.pc = ur->pc;
+ dumpstack();
+ setpri(PriBackground); /* Let the debugger in */
+ for(;;)
+ sched();
+}
+
+void
+dumpstack(void)
+{
+ ulong l, v;
+ int i;
+
+ if(up == 0)
+ return;
+ i = 0;
+ for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){
+ v = *(ulong*)l;
+ if(KTZERO < v && v < (ulong)etext){
+ print("%lux=%lux, ", l, v);
+ if(i++ == 4){
+ print("\n");
+ i = 0;
+ }
+ }
+ }
+}
+
+void
+dumpregs(Ureg *ur)
+{
+ int i;
+ ulong *l;
+ if(up) {
+ print("registers for %s %ld\n", up->text, up->pid);
+ if(ur->usp < (ulong)up->kstack ||
+ ur->usp > (ulong)up->kstack+KSTACK)
+ print("invalid stack ptr\n");
+ }
+ else
+ print("registers for kernel\n");
+
+ l = &ur->cause;
+ for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2)
+ print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]);
+}
+
+static void
+linkproc(void)
+{
+ spllo();
+ (*up->kpfun)(up->arg);
+ pexit("", 0);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+ p->sched.pc = (ulong)linkproc;
+ p->sched.sp = (ulong)p->kstack+KSTACK;
+
+ p->kpfun = func;
+ p->arg = arg;
+}
+
+void
+setpanic(void)
+{
+ consoleprint = 1;
+iprint("panic\n");
+}
+
+void
+dumplongs(char*, ulong*, int)
+{
+}
diff --git a/os/cerf405/uart.c b/os/cerf405/uart.c
new file mode 100644
index 00000000..3d90f5ad
--- /dev/null
+++ b/os/cerf405/uart.c
@@ -0,0 +1,139 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+/*
+ * uart for initial port
+ */
+
+typedef struct Uartregs Uartregs;
+struct Uartregs {
+ uchar rbr;
+#define thr rbr
+#define dll rbr
+ uchar ier;
+#define dlm ier
+ uchar fcr;
+#define iir fcr
+ uchar lcr;
+ uchar mcr;
+ uchar lsr;
+ uchar msr;
+ uchar scr;
+};
+
+#define UARTREGS(n) ((Uartregs*)(PHYSUART0+(n)*0x100))
+
+enum {
+ /* ier */
+ Edssi= 1<<3,
+ Elsi= 1<<2,
+ Etbei= 1<<1,
+ Erbfi= 1<<0,
+
+ /* iir */
+ Fci0= 0<<4,
+ Fci3= 3<<4,
+ Ipl= 7<<1,
+ Ip= 1<<0,
+
+ /* fcr */
+ Rftl1= 0<<6, /* receiver trigger level 1, 16, 32, 56 */
+ Rftl16= 1<<6,
+ Rftl32= 2<<6,
+ Rftl56= 3<<6,
+ Dms= 1<<3, /* =0, single transfer; =1, multiple transfers */
+ Tfr= 1<<2, /* transmitter fifo reset */
+ Rfr= 1<<1, /* receiver fifo reset */
+ Fifoe= 1<<0, /* =0, disable fifos; =1, enable fifos */
+
+ /* lcr */
+ Dlab= 1<<7, /* =0, normal; =1, dll/dlm visible */
+ Sb= 1<<6, /* set break */
+ Sp= 1<<5, /* =1, enable sticky parity */
+ Eps= 1<<4, /* =1, generate even parity */
+ Pen= 1<<3, /* =1, enable parity checking */
+ Sbs= 1<<2, /* =0, 1 stop bit; =1, 1.5 or 2 stop bits (see Wls) */
+ Wls= 3<<0, /* set to nbit-5 for nbit characters */
+
+ /* mcr */
+ Afc= 1<<5, /* =1, auto flow control enabled */
+ Loop= 1<<4, /* =1, loop back mode enabled */
+ Out2= 1<<3, /* =0, OUT2# active */
+ Out1= 1<<2, /* =0, OUT1# active */
+ Rts= 1<<1, /* =0, RTS# inactive (1); =1, RTS# active (0) */
+ Dtr= 1<<0, /* =0, DTS# inactive; =1, DTS# active */
+
+ /* lsr */
+ Rfe= 1<<7, /* error instances in fifo */
+ Temt= 1<<6, /* transmitter empty */
+ Thre= 1<<5, /* transmitter holding register/fifo empty */
+ Be= 1<<4, /* break (interrupt) */
+ Fe= 1<<3, /* framing error */
+ Pe= 1<<2, /* parity error */
+ Oe= 1<<1, /* overrun error */
+ Dr= 1<<0, /* receiver data ready */
+
+ /* msr */
+ Dcd= 1<<7, /* follows Out2 */
+ Ri= 1<<6, /* follows Out1 */
+ Dsr= 1<<5, /* follows Dtr */
+ Cts= 1<<4, /* follows Rts */
+ Ddcd= 1<<3, /* Dcd input changed */
+ Teri= 1<<2, /* Ri changed from 0 to 1 */
+ Ddsr= 1<<1, /* Dsr input changed */
+ Dcts= 1<<0, /* Cts input changed */
+};
+
+void (*serwrite)(char*, int) = uartputs;
+
+void
+uartinstall(void)
+{
+}
+
+void
+uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int))
+{
+}
+
+void
+uartputc(int c)
+{
+ Uartregs *r;
+
+ if(c == 0)
+ return;
+ r = UARTREGS(0);
+ while((r->lsr & Thre) == 0)
+ {}
+ r->thr = c;
+ if(c == '\n')
+ while((r->lsr & Thre) == 0) /* flush xmit fifo */
+ {}
+}
+
+void
+uartputs(char *data, int len)
+{
+ int s;
+
+// if(!uartspcl && !redirectconsole)
+// return;
+ s = splhi();
+ while(--len >= 0){
+ if(*data == '\n')
+ uartputc('\r');
+ uartputc(*data++);
+ }
+ splx(s);
+}
+
+void
+uartwait(void)
+{
+}
diff --git a/os/cerf405/uart.h b/os/cerf405/uart.h
new file mode 100644
index 00000000..28037473
--- /dev/null
+++ b/os/cerf405/uart.h
@@ -0,0 +1,101 @@
+/*
+ * 405EP specific code for its uart.
+ */
+
+#define UR(p,r) ((uchar*)(p))[r]
+#define uartwr(u,r,v) (UR(u->regs,r) = (v))
+#define uartwrreg(u,r,v) (UR(u->regs,r)= (u)->sticky[r] | (v))
+#define uartrdreg(u,r) UR(u->regs,r)
+
+extern void uartsetup(ulong, void*, ulong, char*);
+extern void uartclock(void);
+
+static void
+uartportpower(Uart*, int)
+{
+ /* TO DO: power control */
+}
+
+/*
+ * handle an interrupt to a single uart
+ */
+static void
+uartintrx(Ureg*, void* arg)
+{
+ uartintr(arg);
+}
+
+/*
+ * install the uarts (called by reset)
+ */
+void
+uartinstall(void)
+{
+ static int already;
+
+ if(already)
+ return;
+ already = 1;
+
+ /* first two ports are always there */
+ uartsetup(0, (void*)PHYSUART0, 0, "eia0");
+ intrenable(VectorUART0, uartintrx, uart[0], BUSUNKNOWN, "uart0");
+ uartsetup(1, (void*)PHYSUART1, 0, "eia1");
+ intrenable(VectorUART1, uartintrx, uart[1], BUSUNKNOWN, "uart1");
+ addclock0link(uartclock, 22);
+}
+
+/*
+ * If the UART's receiver can be connected to a DMA channel,
+ * this function does what is necessary to create the
+ * connection and returns the DMA channel number.
+ * If the UART's receiver cannot be connected to a DMA channel,
+ * a -1 is returned.
+ */
+char
+uartdmarcv(int dev)
+{
+
+ USED(dev);
+ return -1;
+}
+
+void
+uartputc(int c)
+{
+ uchar *p;
+
+ if(c == 0)
+ return;
+ p = (uchar*)PHYSUART0;
+ while((UR(p,Lstat) & Outready) == 0){
+ ;
+ }
+ UR(p,Data) = c;
+ eieio();
+ if(c == '\n')
+ while((UR(p,Lstat) & Outready) == 0){ /* let fifo drain */
+ ;
+ }
+}
+
+void
+uartputs(char *data, int len)
+{
+ int s;
+
+// if(!uartspcl && !redirectconsole)
+// return;
+ s = splhi();
+ while(--len >= 0){
+ if(*data == '\n')
+ uartputc('\r');
+ uartputc(*data++);
+ }
+ splx(s);
+}
+
+void
+uartwait(void)
+{
+}