summaryrefslogtreecommitdiff
path: root/os/cerf250
diff options
context:
space:
mode:
Diffstat (limited to 'os/cerf250')
-rw-r--r--os/cerf250/NOTICE2
-rw-r--r--os/cerf250/README44
-rw-r--r--os/cerf250/archcerf.c266
-rw-r--r--os/cerf250/cerf163
-rw-r--r--os/cerf250/dat.h135
-rw-r--r--os/cerf250/devpcf8563.c371
-rw-r--r--os/cerf250/ether91c111.c1056
-rw-r--r--os/cerf250/fns.h168
-rw-r--r--os/cerf250/io.h1
-rw-r--r--os/cerf250/main.c351
-rw-r--r--os/cerf250/mem.h174
-rw-r--r--os/cerf250/mkfile102
-rw-r--r--os/cerf250/uart.h118
13 files changed, 2951 insertions, 0 deletions
diff --git a/os/cerf250/NOTICE b/os/cerf250/NOTICE
new file mode 100644
index 00000000..7c34dd62
--- /dev/null
+++ b/os/cerf250/NOTICE
@@ -0,0 +1,2 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved.
+Cerfcube 250 Inferno port Copyright © 2000,2003,2004 Vita Nuova Holdings Limited. All rights reserved.
diff --git a/os/cerf250/README b/os/cerf250/README
new file mode 100644
index 00000000..ee1ac2d1
--- /dev/null
+++ b/os/cerf250/README
@@ -0,0 +1,44 @@
+Booting Inferno on a Cerfboard 250
+
+This is a preliminary version (work in progress) of Inferno
+on an Intrinsyc Cerfboard 250 (without daughterboard[s]).
+It and ../pxa were initially the results of a fairly idle `afternoon and an evening' port.
+A little work has been done on it since then.
+Updates will be available shortly to complete peripheral support
+(at least for the Cerfboard 250), and provide suspend mode.
+
+It allows Inferno to boot up and take resources from the net,
+chatting on the console. I2C to the PCF8563 clock and EEPROMs is
+also supported.
+
+Substitute appropriate your own directory's name for /usr/inferno
+in the following.
+
+Build the /usr/inferno/os/cerf250 kernel into /usr/inferno/os/cerf250/icerf:
+ mk
+It uses common PXA25x code in ../pxa, as well as ../port etc.
+
+Make that icerf file available to the cerf cube by tftp. How you do that depends on
+your host system.
+
+It should then be easy:
+
+1. Reset the cerf cube (power off/on), and quickly, during `hit a key ...'
+ hit a key.
+
+2. type
+ network.start()
+ download 10.0.0.1 "/usr/inferno/os/cerf250/icerf" 0xa0020000
+ with appropriate substitution for boot server and file name.
+
+3. on success
+ jump 0xa0020020
+
+it should run.
+
+once you're happy with it, the kernel image can replace the Linux one in flash.
+i plan, however, to use my sqz code to compress it by about 50% with
+fast decompression.
+
+forsyth@vitanuova.com
+Fri Mar 19 16:42:07 GMT 2004
diff --git a/os/cerf250/archcerf.c b/os/cerf250/archcerf.c
new file mode 100644
index 00000000..4ee9f4e7
--- /dev/null
+++ b/os/cerf250/archcerf.c
@@ -0,0 +1,266 @@
+/*
+ * Intrinsyc Cerfboard 250
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "draw.h"
+#include <memdraw.h>
+
+#include "../port/netif.h"
+#include "etherif.h"
+#include "../port/flashif.h"
+
+enum {
+ /* Cerf250 GPIO assignment */
+ GPIO_LED0_o = 4, /* active high */
+ GPIO_nCFD_i= 14, /* compact flash detect, active low: falling edge */
+
+ Maxmac= 4, /* number of MAC addresses taken from EEPROM */
+};
+
+static uchar macaddrs[Maxmac][Eaddrlen];
+
+void
+archreset(void)
+{
+ GpioReg *g = GPIOREG;
+
+ g->grer[0] = 0;
+ g->grer[1] = 0;
+ g->grer[2] = 0;
+ g->gfer[0] = 0;
+ g->gfer[1] = 0;
+ g->gfer[2] = 0;
+ g->gedr[0] = g->gedr[0];
+ g->gedr[1] = g->gedr[1];
+ g->gedr[2] = g->gedr[2];
+
+ g->gafr[2] |= GPAF(GPIO_FFRXD_1_i, 1);
+ g->gafr[2] |= GPAF(GPIO_FFTXD_2_o, 2);
+ g->gpdr[0] |= GPB(GPIO_LED0_o);
+ g->gpdr[1] |= GPB(GPIO_FFTXD_2_o);
+ g->gpsr[0] = GPB(GPIO_LED0_o);
+
+ uartdebuginit();
+}
+
+void
+ledset(int n)
+{
+ if(n)
+ GPIOREG->gpsr[0] = GPB(GPIO_LED0_o);
+ else
+ GPIOREG->gpcr[0] = GPB(GPIO_LED0_o);
+}
+
+void
+archconfinit(void)
+{
+ int w;
+
+ conf.topofmem = PHYSMEM0+64*MB;
+// w = PMGRREG->ppcr & 0x1f;
+ m->cpuhz = CLOCKFREQ*(27*2*2);
+}
+
+void
+archuartpower(int, int)
+{
+}
+
+void
+kbdinit(void)
+{
+}
+
+void
+archreboot(void)
+{
+ dcflushall();
+ ledset(1);
+// GPIOREG->gedr = 1<<0;
+ mmuputctl(mmugetctl() & ~CpCaltivec); /* restore bootstrap's vectors */
+// RESETREG->rsrr = 1; /* software reset */
+ for(;;)
+ spllo();
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
+
+/*
+ * 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)
+{
+ if(bank != 0)
+ return -1;
+ f->type = "cfi16";
+ f->addr = KADDR(FLASHMEM);
+ f->size = 0;
+ f->width = 4;
+ f->interleave = 1;
+ return 0;
+}
+
+/*
+ * set ether parameters: the contents should be derived from EEPROM or NVRAM
+ */
+int
+archether(int ctlno, Ether *ether)
+{
+ if(ctlno > 0)
+ return -1;
+ sprint(ether->type, "91c111");
+ ether->mem = PHYSCS1;
+ ether->nopt = 0;
+ ether->port = 0x300; /* there isn't an ether EEPROM; use chip's default */
+ ether->irq = 21; /* GPIO */
+ ether->itype = GPIOrising; /* active high */
+// gpioreserve(ether->irq);
+// gpioconfig(ether->irq, Gpio_gpio | Gpio_in);
+ memmove(ether->ea, macaddrs[ctlno], Eaddrlen);
+ return 1;
+}
+
+/*
+ * pcmcia
+ */
+int
+pcmpowered(int slotno)
+{
+ if(slotno)
+ return 0;
+ return 3;
+}
+
+void
+pcmpower(int slotno, int on)
+{
+ USED(slotno, on);
+}
+
+void
+pcmreset(int slot)
+{
+ if(slot != 0)
+ return;
+// GPIOREG->gpsr = CFReset;
+// delay(100);
+// GPIOREG->gpcr = CFReset;
+}
+
+int
+pcmpin(int slot, int type)
+{
+ if(slot)
+ return -1;
+ return -1;
+// switch(type){
+// case PCMready:
+// return CFRdypin;
+// case PCMeject:
+// return CFnCDxpin;
+// case PCMstschng:
+// return -1;
+// }
+}
+
+void
+pcmsetvpp(int slot, int vpp)
+{
+ USED(slot, vpp);
+}
+
+/*
+ * boot environment in eeprom
+ */
+
+enum {
+ EEpromHdr= 8, /* bytes */
+ Envitemsize= 64,
+ Ekeysize= 48,
+};
+
+static I2Cdev eedev;
+static struct {
+ uchar buf[Envitemsize];
+ 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;
+ int mac;
+
+ eedev.addr = 0x56;
+ 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(memcmp(bootenv.buf, "MACAD", 5) == 0){ /* only look for MAC addresses now */
+ mac = bootenv.buf[5] - '0';
+ if(mac >= 0 && mac < Maxmac){
+ l = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off);
+ if(l < 0)
+ break;
+ if(l == Eaddrlen)
+ memmove(macaddrs[mac], bootenv.buf, Eaddrlen);
+ off += l+2;
+ continue;
+ }
+ }
+ l = eepromitem(nil, 0, &off); /* skip value */
+ if(l < 0)
+ break;
+ off += l+2; /* 2 byte crc */
+ }
+}
diff --git a/os/cerf250/cerf b/os/cerf250/cerf
new file mode 100644
index 00000000..20b52181
--- /dev/null
+++ b/os/cerf250/cerf
@@ -0,0 +1,163 @@
+dev
+ root
+ cons archcerf
+ env
+ mnt
+ pipe
+ prog
+# rtc
+ pcf8563 i2c
+ srv
+ dup
+ ssl
+ cap
+ sign
+
+# draw screen
+# pointer
+ uart
+ ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux
+ flash
+# ftl
+# pcmcia cis
+# ata
+ ether netif netaux
+
+# cerf
+# kprof
+ i2c i2c
+
+ip
+ il
+ tcp
+ udp
+# rudp
+# igmp
+ ipifc
+ icmp
+ icmp6
+# ipmux
+
+link
+ flashcfi16
+ ether91c111 # ethermii
+ ethermedium
+
+lib
+ interp
+# tk
+# draw
+# memlayer
+# memdraw
+ keyring
+ math
+ sec
+ mp
+ kern
+
+mod
+ math
+ sys
+# draw
+# tk
+ 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 main_pool_pcnt = 50;
+ int heap_pool_pcnt = 50;
+ int image_pool_pcnt = 0;
+ int cflag = 0; /* for JIT */
+
+ int consoleprint = 1;
+ char debug_keys = 1;
+ int panicreset = 0;
+ void screeninit(void){}
+
+init
+ cerfinit
+
+root
+ /chan /
+ /dev /
+ /env /
+ /fd /
+ /net /
+ /net.alt /
+ /nvfs /
+ /prog /
+ /root /
+ /nvfs /
+ /osinit.dis
+ /tmp /
+ /dis
+ /dis/lib
+ /dis/disk
+
+# dos file system
+ /dis/dossrv.dis
+ /dis/lib/arg.dis
+ /dis/lib/styx.dis
+ /dis/lib/string.dis
+ /dis/lib/daytime.dis
+
+ /dis/disk/format.dis
+
+# For development work:
+ /dis/sh.dis /dis/tiny/sh.dis
+ /dis/ls.dis
+ /dis/cat.dis
+ /dis/bind.dis
+ /dis/mount.dis
+ /dis/pwd.dis
+ /dis/echo.dis
+ /dis/cd.dis
+ /dis/xd.dis
+ /dis/cp.dis
+ /dis/mkdir.dis
+ /dis/rm.dis
+ /dis/p.dis
+ /dis/ps.dis
+ /dis/lib/readdir.dis
+ /dis/lib/workdir.dis
+ /dis/lib/daytime.dis
+ /dis/lib/auth.dis
+ /dis/lib/ssl.dis
+ /dis/lib/bufio.dis
+ /dis/lib/string.dis
+# /dis/pcmcia.dis /usr/forsyth/pcmcia.dis
+
+ /n/remote
+ /n/local
+ /n/client
+ /n/rdbg
+ /n/dump
+ /n/disk
+ /n/kfs
+# Authentication
+ /nvfs/default /usr/inferno/keyring/default
diff --git a/os/cerf250/dat.h b/os/cerf250/dat.h
new file mode 100644
index 00000000..553c0cb0
--- /dev/null
+++ b/os/cerf250/dat.h
@@ -0,0 +1,135 @@
+typedef struct Conf Conf;
+typedef struct Dma Dma;
+typedef struct FPU FPU;
+typedef struct FPenv FPenv;
+typedef struct Label Label;
+typedef struct Lock Lock;
+typedef struct Mach Mach;
+typedef struct Ureg Ureg;
+typedef struct ISAConf ISAConf;
+typedef struct PCMmap PCMmap;
+typedef struct PCMslot PCMslot;
+
+typedef ulong Instr;
+
+struct Conf
+{
+ ulong nmach; /* processors */
+ ulong nproc; /* processes */
+ ulong npage0; /* total physical pages of memory */
+ ulong npage1; /* total physical pages of memory */
+ ulong topofmem; /* highest physical address + 1 */
+ 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 */
+
+ int useminicache; /* use mini cache: screen.c/lcd.c */
+ int textwrite; /* writeable text segment, for debug */
+ int portrait; /* display orientation */
+};
+
+#define NISAOPT 8
+struct ISAConf {
+ char type[KNAMELEN];
+ ulong port;
+ ulong irq;
+ int itype;
+ ulong dma;
+ ulong mem;
+ ulong size;
+ ulong freq;
+
+ int nopt;
+ char *opt[NISAOPT];
+};
+
+/*
+ * FPenv.status
+ */
+enum
+{
+ FPINIT,
+ FPACTIVE,
+ FPINACTIVE,
+};
+
+struct FPenv
+{
+ ulong status;
+ ulong control;
+ ushort fpistate; /* emulated fp */
+ ulong regs[8][3]; /* emulated fp */
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct FPU
+{
+ FPenv env;
+};
+
+struct Label
+{
+ ulong sp;
+ ulong pc;
+};
+
+struct Lock
+{
+ ulong key;
+ ulong sr;
+ ulong pc;
+ int pri;
+};
+
+#include "../port/portdat.h"
+
+/*
+ * machine dependent definitions not used by ../port/portdat.h
+ */
+struct Mach
+{
+ /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+ ulong splpc; /* pc of last caller to splhi */
+
+ /* ordering from here on irrelevant */
+
+ int machno; /* physical id of processor */
+ 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 */
+ ulong cpuhz;
+
+ /* stacks for exceptions */
+ ulong fiqstack[4];
+ ulong irqstack[4];
+ ulong abtstack[4];
+ ulong undstack[4];
+
+ int stack[1];
+};
+
+#define MACHP(n) (n == 0 ? (Mach*)(MACHADDR) : (Mach*)0)
+
+extern Mach *m;
+extern Proc *up;
+
+typedef struct MemBank {
+ uint pbase;
+ uint plimit;
+ uint vbase;
+ uint vlimit;
+} MemBank;
+
+/*
+ * Layout at virtual address 0.
+ */
+typedef struct Vectorpage {
+ void (*vectors[8])(void);
+ uint vtable[8];
+} Vectorpage;
+extern Vectorpage *page0;
diff --git a/os/cerf250/devpcf8563.c b/os/cerf250/devpcf8563.c
new file mode 100644
index 00000000..f52fe60d
--- /dev/null
+++ b/os/cerf250/devpcf8563.c
@@ -0,0 +1,371 @@
+/*
+ * Philips PCF8563 real-time clock on I⁲C (and compatibles)
+ *
+ * currently this can't coexist with ../pxa/devrtc.c
+ */
+
+#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 csr1;
+ uchar csr2;
+ uchar sec; /* 00-59 and VL */
+ uchar min; /* 00-59 */
+ uchar hour; /* 00-23 */
+ uchar mday; /* 01-31 */
+ uchar wday; /* 0=Sun */
+ uchar mon; /* 1-12 and 1900 bit */
+ uchar year;
+ uchar amin; /* minute alarm */
+ uchar ahour;
+ uchar aday;
+ uchar awday;
+};
+
+enum{
+ Qdir = 0,
+ Qrtc,
+
+ Rtclen= 0x0C+1, /* bytes read and written to timekeeper */
+ VL= 0x80, /* reliable clock data no longer guaranteed */
+};
+
+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 = 0x51;
+ 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 pcf8563devtab = {
+ 'r',
+ "pcf8563",
+
+ 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;
+
+ 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 & 0x7F);
+ rtc.min = getbcd(d.min & 0x7F);
+ rtc.hour = getbcd(d.hour & 0x3F);
+ rtc.mday = getbcd(d.mday & 0x3F);
+ rtc.mon = getbcd(d.mon & 0x1f);
+ rtc.year = getbcd(d.year);
+ if(rtc.mon < 1 || rtc.mon > 12)
+ return 0;
+ if(d.mon & (1<<7))
+ rtc.year += 1900;
+ else
+ rtc.year += 2000;
+ 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/cerf250/ether91c111.c b/os/cerf250/ether91c111.c
new file mode 100644
index 00000000..2a404d93
--- /dev/null
+++ b/os/cerf250/ether91c111.c
@@ -0,0 +1,1056 @@
+/*
+ * SMsC 91c111 ethernet controller
+ * Copyright © 2001,2004 Vita Nuova Holdings Limited. All rights reserved.
+ *
+ * TO DO:
+ * - use ethermii
+ * - use DMA where available
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+/*
+ * chip definitions
+ */
+
+typedef struct Ctlr Ctlr;
+
+enum {
+ SMSC91C11x,
+ SMSC91C110,
+ SMSC91C111,
+ SMSC91C96,
+};
+
+struct Ctlr {
+ Lock;
+ uchar *base;
+ int type;
+ int rev;
+ int hasmii;
+ int phyad;
+ int bank; /* currently selected bank */
+ Block* waiting; /* waiting for space in FIFO */
+
+ ulong collisions;
+ ulong toolongs;
+ ulong tooshorts;
+ ulong aligns;
+ ulong txerrors;
+ int oddworks;
+ int bus32bit;
+};
+
+#define MKREG(bank, off) ((bank << 8) | (off))
+
+enum {
+ /* Bank 0 */
+ Tcr= MKREG(0, 0), /* transmit control */
+ TcrSwfdup= 1<<15, /* switched full duplex */
+ TcrEphLoop= 1<<13, /* internal loopback */
+ TcrStpSqet= 1<<12, /* stop transmission on SQET error */
+ TcrFduplx= 1<<11, /* enable full duplex */
+ TcrMonCsn= 1<<10, /* monitor collision (0 for MII operation) */
+ TcrNoCRC= 1<<8, /* don't add CRC */
+ TcrPadEn= 1<<7, /* pad short frames */
+ TcrForceCol= 1<<2, /* force collision */
+ TcrLoop= 1<<1, /* PHY loopback */
+ TcrTxena= 1<<0, /* enable transmitter */
+ Eph= MKREG(0, 2), /* there are more bits but we don't use them */
+ EphLinkOk= 1<<14,
+ EphCtrRol= 1<<12, /* counter roll over; clear by reading Ecr */
+ Rcr= MKREG(0, 4), /* receive control */
+ RcrSoftRst= 1<<15,
+ RcrFiltCar= 1<<14,
+ RcrAbortEnb= 1<<13,
+ RcrStripCRC= 1<<9,
+ RcrRxEn= 1<<8,
+ RcrAlmul= 1<<2, /* ~=0, accept all multicast frames (=0, match multicast table) */
+ RcrPrms= 1<<1, /* promiscuous mode */
+ RcrRxAbort= 1<<0, /* set if receive frame longer than 2k bytes */
+ Ecr= MKREG(0, 6), /* counter */
+ EcrExcDeferred= 0xF<<12, /* excessively deferred Tx */
+ EcrDeferred= 0xF<<8, /* deferred Tx */
+ EcrMultCol= 0xF<<4, /* multiple collisions */
+ EcrCollision= 0xF<<0, /* single collision */
+ Mir= MKREG(0, 8), /* memory information */
+ Mcr= MKREG(0, 0xA), /* memory config (91cxx) */
+ Rpcr= Mcr, /* receive/phy control (91c111) */
+
+ /* Bank 1 */
+ Config= MKREG(1, 0),
+ CfgMiiSelect= 1<<15, /* 91c110 */
+ CfgEphPowerEn= CfgMiiSelect, /* =1, powered (after reset MMU); =0, low power mode (91c111) */
+ CfgNoWait= 1<<12, /* don't request additional wait states */
+ CfgSetSqlch= 1<<9, /* 91cxx */
+ CfgGpcntrl= 1<<9, /* general purpose output (CNTRL), perhaps power-enable (91c111) */
+ CfgAuiSelect= 1<<8, /* 91cxx */
+ CfgExtPhy= 1<<8, /* enable external PHY/MII (91c111) */
+ Cfg16Bit= 1<<7, /* 91cxx */
+ BaseAddress= MKREG(1, 2),
+ Iaddr0_1= MKREG(1, 4),
+ Iaddr2_3= MKREG(1, 6),
+ Iaddr4_5= MKREG(1, 8),
+ Gpr= MKREG(1, 0xA), /* general purpose reg (EEPROM interface) */
+ Control= MKREG(1, 0xC), /* control register */
+ CtlRcvBad= 1<<14, /* allow bad CRC packets through */
+ CtlAutoRelease= 1<<11, /* transmit pages released automatically w/out interrupt */
+ CtlLeEnable= 1<<7, /* link error enable */
+ CtlCrEnable= 1<<6, /* counter roll over enable */
+ CtlTeEnable= 1<<5, /* transmit error enable */
+ CtlEeSelect= 1<<2, /* EEPROM select */
+ CtlReload= 1<<1, /* read EEPROM and update relevant registers */
+ CtlStore= 1<<0, /* store relevant registers in EEPROM */
+
+ /* Bank 2 */
+ Mmucr= MKREG(2, 0), /* MMU command */
+ McrAllocTx= 1<<5, /* allocate space for outgoing packet */
+ McrReset= 2<<5, /* reset to initial state */
+ McrReadFIFO= 3<<5, /* remove frame from top of FIFO */
+ McrRemove= 4<<5, /* remove and release top of Rx FIFO */
+ McrFreeTx= 5<<5, /* release specific packet (eg, packets done Tx) */
+ McrEnqueue= 6<<5, /* enqueue packet number to Tx FIFO */
+ McrResetTx= 7<<5, /* reset both Tx FIFOs */
+ McrBusy= 1<<0,
+ ArrPnr= MKREG(2, 2), /* Pnr (low byte), Arr (high byte) */
+ ArrFailed= 1<<15,
+ FifoPorts= MKREG(2, 4),
+ FifoRxEmpty= 1<<15,
+ FifoTxEmpty= 1<<7,
+ Pointer= MKREG(2, 6),
+ PtrRcv= 1<<15,
+ PtrAutoIncr= 1<<14,
+ PtrRead= 1<<13,
+ PtrEtEn= 1<<12,
+ PtrNotEmpty= 1<<11,
+ Data= MKREG(2, 8),
+ Interrupt= MKREG(2, 0xC), /* status/ack (low byte), mask (high byte) */
+ IntMii= 1<<7, /* PHY/MII state change */
+ IntErcv= 1<<6, /* early receive interrupt (received > Ercv threshold) */
+ IntEph= 1<<5, /* ethernet protocol interrupt */
+ IntRxOvrn= 1<<4, /* overrun */
+ IntAlloc= 1<<3, /* allocation complete */
+ IntTxEmpty= 1<<2, /* TX FIFO now empty */
+ IntTx= 1<<1, /* transmit done */
+ IntRcv= 1<<0, /* packet received */
+ IntrMask= MKREG(2, 0xD),
+ IntrMaskShift= 8, /* shift for Int... values to mask position in 16-bit register */
+ IntrMaskField= 0xFF00,
+
+ /* Bank 3 */
+ Mt0_1= MKREG(3, 0), /* multicast table */
+ Mt2_3= MKREG(3, 2),
+ Mt4_5= MKREG(3, 4),
+ Mt6_7= MKREG(3, 6),
+ Mgmt= MKREG(3, 8), /* management interface (MII) */
+ MgmtMdo= 1<<0, /* MDO pin */
+ MgmtMdi= 1<<1, /* MDI pin */
+ MgmtMclk= 1<<2, /* drive MDCLK */
+ MgmtMdoEn= 1<<3, /* MDO driven when high, tri-stated when low */
+ Revision= MKREG(3, 0xA),
+ Ercv= MKREG(3, 0xC), /* early receive */
+
+ /* Bank 4 (91cxx only) */
+ EcsrEcor= MKREG(4, 0), /* status and option registers */
+
+ /* all banks */
+ BankSelect= MKREG(0, 0xe),
+};
+
+enum {
+ /* receive frame status word (p 38) */
+ RsAlgnErr= 1<<15,
+ RsBroadcast= 1<<14,
+ RsBadCRC= 1<<13,
+ RsOddFrame= 1<<12,
+ RsTooLong= 1<<11,
+ RsTooShort= 1<<10,
+ RsMulticast= 1<<1,
+ RsError= RsBadCRC | RsAlgnErr | RsTooLong | RsTooShort,
+
+ Framectlsize= 6,
+};
+
+static void miiw(Ctlr *ctlr, int regad, int val);
+static int miir(Ctlr *ctlr, int regad);
+
+/*
+ * architecture dependent section - collected here in case
+ * we want to port the driver
+ */
+
+#define PHYMIIADDR_91C110 3
+#define PHYMIIADDR_91C111 0
+
+#define llregr(ctlr, reg) (*(ushort*)(ctlr->base + (reg)))
+#define llregr32(ctlr, reg) (*(ulong*)(ctlr->base + (reg)))
+#define llregw(ctlr, reg, val) (*(ushort*)(ctlr->base + (reg)) = (val))
+
+static void
+adinit(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ // TODO: code to turn on device clocks
+ ctlr->base = (uchar*)mmuphysmap(PHYSCS1, 0x100000) + ether->port;
+iprint("adinit: %8.8lux -> %8.8lux mcs0=%8.8lux\n", (ulong)ctlr->base, PADDR(ctlr->base), MEMCFGREG->msc0);
+{ulong v; v = *(ulong*)ctlr->base; iprint("value=%8.8lux\n", v);}
+ ctlr->bus32bit = 1;
+}
+
+static void
+adsetfd(Ctlr *ctlr)
+{
+ miiw(ctlr, 0x18, miir(ctlr, 0x18) | (1 << 5));
+}
+
+/*
+ * architecture independent section
+ */
+
+static ushort
+regr(Ctlr *ctlr, int reg)
+{
+ int bank;
+ ushort val;
+
+ bank = reg >> 8;
+ if(ctlr->bank != bank){
+ ctlr->bank = bank;
+ llregw(ctlr, BankSelect, bank);
+ }
+ val = llregr(ctlr, reg & 0xff);
+ return val;
+}
+
+static ulong
+regr32(Ctlr *ctlr, int reg)
+{
+ int bank;
+ ulong val;
+
+ bank = reg >> 8;
+ if(ctlr->bank != bank){
+ ctlr->bank = bank;
+ llregw(ctlr, BankSelect, bank);
+ }
+ val = llregr32(ctlr, reg & 0xff);
+ return val;
+}
+
+static void
+regw(Ctlr *ctlr, int reg, ushort val)
+{
+ int bank;
+
+ bank = reg >> 8;
+ if(ctlr->bank != bank){
+ ctlr->bank = bank;
+ llregw(ctlr, BankSelect, bank);
+ }
+ llregw(ctlr, reg & 0xff, val);
+}
+
+static void
+regwdatam(Ctlr *ctlr, ushort *data, int ns)
+{
+ int bank;
+ ushort *faddr;
+
+ bank = Data >> 8;
+ if(ctlr->bank != bank){
+ ctlr->bank = bank;
+ llregw(ctlr, BankSelect, bank);
+ }
+ faddr = (ushort*)(ctlr->base + (Data & 0xff));
+ while(ns-- > 0){
+ *faddr = *data;
+ data++;
+ }
+}
+
+static void
+regrdatam(Ctlr *ctlr, void *data, int nb)
+{
+ int bank;
+ ushort *f, *t;
+ int laps, ns;
+
+ bank = Data >> 8;
+ if(ctlr->bank != bank){
+ ctlr->bank = bank;
+ llregw(ctlr, BankSelect, bank);
+ }
+
+ if((ulong)data & 3)
+ iprint("bad buffer alignment\n");
+
+ t = data;
+ f = (ushort*)(ctlr->base + (Data & 0xff));
+ ns = nb >> 1;
+ laps = ns / 8;
+ switch(ns & 7){ /* Duff's device */
+ do {
+ *t++ = *f;
+ case 7: *t++ = *f;
+ case 6: *t++ = *f;
+ case 5: *t++ = *f;
+ case 4: *t++ = *f;
+ case 3: *t++ = *f;
+ case 2: *t++ = *f;
+ case 1: *t++ = *f;
+ case 0:
+ ;
+ } while(laps-- > 0);
+ }
+}
+
+static void
+regrdatam32(Ctlr *ctlr, void *data, int nb)
+{
+ int bank;
+ ulong *f, *t;
+ int laps, nw;
+
+ bank = Data >> 8;
+ if(ctlr->bank != bank){
+ ctlr->bank = bank;
+ llregw(ctlr, BankSelect, bank);
+ }
+
+ if((ulong)data & 3)
+ iprint("bad buffer alignment\n");
+
+ t = data;
+ f = (ulong*)(ctlr->base + (Data & 0xff));
+ nw = nb>>2;
+ laps = nw / 8;
+ switch(nw & 7){ /* Duff's device */
+ do {
+ *t++ = *f;
+ case 7: *t++ = *f;
+ case 6: *t++ = *f;
+ case 5: *t++ = *f;
+ case 4: *t++ = *f;
+ case 3: *t++ = *f;
+ case 2: *t++ = *f;
+ case 1: *t++ = *f;
+ case 0:
+ ;
+ } while(laps-- > 0);
+ }
+}
+
+static void
+regor(Ctlr *ctlr, int reg, ushort val)
+{
+ int bank;
+
+ bank = reg >> 8;
+ if(ctlr->bank != bank){
+ ctlr->bank = bank;
+ llregw(ctlr, BankSelect, bank);
+ }
+ reg &= 0xff;
+ llregw(ctlr, reg, llregr(ctlr, reg) | val);
+}
+
+static void
+regclear(Ctlr *ctlr, int reg, ushort val)
+{
+ int bank;
+
+ bank = reg >> 8;
+ if(ctlr->bank != bank){
+ ctlr->bank = bank;
+ llregw(ctlr, BankSelect, bank);
+ }
+ reg &= 0xff;
+ llregw(ctlr, reg, llregr(ctlr, reg) & ~val);
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+ Ctlr *ctlr;
+ char *p;
+ int len;
+
+ if(n == 0)
+ return 0;
+
+ ctlr = ether->ctlr;
+ p = smalloc(READSTR);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ len = snprint(p, READSTR, "Overflow: %ud\n", ether->overflows);
+ len += snprint(p+len, READSTR, "Soft Overflow: %ud\n", ether->soverflows);
+ len += snprint(p+len, READSTR, "Transmit Error: %lud\n", ctlr->txerrors);
+ len += snprint(p+len, READSTR-len, "CRC Error: %ud\n", ether->crcs);
+ len += snprint(p+len, READSTR-len, "Collision: %lud\n", ctlr->collisions);
+ len += snprint(p+len, READSTR-len, "Align: %lud\n", ctlr->aligns);
+ len += snprint(p+len, READSTR-len, "Too Long: %lud\n", ctlr->toolongs);
+ snprint(p+len, READSTR-len, "Too Short: %lud\n", ctlr->tooshorts);
+
+ n = readstr(offset, a, n, p);
+ poperror();
+ free(p);
+
+ return n;
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+ int r;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+ ilock(ctlr);
+ r = regr(ctlr, Rcr);
+ if(on)
+ r |= RcrPrms;
+ else
+ r &= ~RcrPrms;
+ regw(ctlr, Rcr, r);
+ iunlock(ctlr);
+}
+
+static void
+attach(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+
+ /*
+ * enable transmit and receive
+ */
+ regw(ctlr, Interrupt, (IntMii | IntTx | IntRcv | IntRxOvrn)<<IntrMaskShift);
+ regor(ctlr, Rcr, RcrRxEn);
+ regor(ctlr, Tcr, TcrTxena);
+}
+
+static void
+pointtotxpacket(Ctlr *ctlr, int pkt, int read) // read=PtrRead in failure case
+{
+ ushort junk;
+
+ pkt &= 0x3F;
+ regw(ctlr, ArrPnr, pkt);
+ while(regr(ctlr, Pointer) & PtrNotEmpty)
+ ;
+ regw(ctlr, Pointer, read | PtrAutoIncr);
+ junk = llregr(ctlr, BankSelect); /* possible wait state */
+ USED(junk);
+}
+
+static void
+pointtorxpacket(Ctlr *ctlr, int offset)
+{
+ ushort junk;
+
+ regw(ctlr, Pointer, PtrRcv | PtrAutoIncr | PtrRead | offset);
+ junk = llregr(ctlr, BankSelect); /* possible wait state */
+ USED(junk);
+}
+
+static void
+mmucommand(Ctlr *ctlr, ushort cmd)
+{
+ while(regr(ctlr, Mmucr) & McrBusy) // should signal free resource
+ ;
+ regw(ctlr, Mmucr, cmd); // do the work
+}
+
+static void
+txloadpacket(Ether *ether)
+{
+ Ctlr *ctlr;
+ int pkt;
+ Block *b;
+ ushort lastw;
+ int lenb, lenw;
+ int odd;
+
+ ctlr = ether->ctlr;
+ b = ctlr->waiting;
+ ctlr->waiting = nil;
+ if(b == nil)
+ return; /* shouldn't happen */
+ pkt = regr(ctlr, ArrPnr); /* get packet number presumably just allocated */
+ if(pkt & 0xC0){
+ print("smc91c111: invalid packet number\n");
+ freeb(b);
+ return;
+ }
+
+ pointtotxpacket(ctlr, pkt, 0);
+
+ lenb = BLEN(b);
+ odd = lenb & 1;
+ lenw = lenb >> 1;
+ regw(ctlr, Data, 0); // status word padding
+ regw(ctlr, Data, (lenw << 1) + Framectlsize);
+ regwdatam(ctlr, (ushort*)b->rp, lenw); // put packet into 91cxxx memory
+ lastw = 0x1000;
+ if(odd){
+ lastw |= 0x2000; /* odd byte flag in control byte */
+ lastw |= b->rp[lenb - 1];
+ }
+ regw(ctlr, Data, lastw);
+ mmucommand(ctlr, McrEnqueue); // chip now owns buff
+ freeb(b);
+ regw(ctlr, Interrupt, (regr(ctlr, Interrupt) & IntrMaskField) | (IntTxEmpty << IntrMaskShift));
+}
+
+static void
+txstart(Ether *ether)
+{
+ Ctlr *ctlr;
+ int n;
+
+ ctlr = ether->ctlr;
+ if(ctlr->waiting != nil) /* allocate pending; must wait for that */
+ return;
+ for(;;){
+ if((ctlr->waiting = qget(ether->oq)) == nil)
+ break;
+ /* ctlr->waiting is a new block to transmit: allocate space */
+ n = (BLEN(ctlr->waiting) & ~1) + Framectlsize; /* Framectlsize includes odd byte, if any */
+ mmucommand(ctlr, McrAllocTx | (n >> 8));
+ if(regr(ctlr, ArrPnr) & ArrFailed){
+ regw(ctlr, Interrupt, (regr(ctlr, Interrupt) & IntrMaskField) | (IntAlloc << IntrMaskShift));
+ break;
+ }
+ txloadpacket(ether);
+ }
+}
+
+static void
+transmit(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(ctlr);
+ txstart(ether);
+ iunlock(ctlr);
+}
+
+static void
+process(Ether *ether)
+{
+ Ctlr *ctlr;
+ int status, intrreg, intr, mask, fifo;
+ int pkt;
+ ulong data;
+ int count, len, alen;
+ Block *b;
+
+ ctlr = ether->ctlr;
+
+Recheck:
+ intrreg = regr(ctlr, Interrupt);
+ regw(ctlr, Interrupt, 0);
+ mask = intrreg >> IntrMaskShift;
+ intr = intrreg & mask;
+ if(intr == 0){
+ regw(ctlr, Interrupt, mask<<IntrMaskShift);
+ return;
+ }
+
+ if(intr & IntAlloc){
+ regw(ctlr, Interrupt, IntAlloc);
+ intr &= ~IntAlloc;
+ if(ctlr->waiting)
+ txloadpacket(ether);
+ mask &= ~IntAlloc;
+ mask |= IntTxEmpty;
+ }
+
+ if(intr & IntRxOvrn){
+ regw(ctlr, Interrupt, IntRxOvrn);
+ intr &= ~IntRxOvrn;
+ ether->overflows++;
+ }
+ if(intr & IntRcv){
+ fifo = regr(ctlr, FifoPorts);
+ while((fifo & FifoRxEmpty) == 0){
+ ether->inpackets++;
+ pointtorxpacket(ctlr, 0);
+ data = regr32(ctlr, Data);
+ status = data & 0xFFFF;
+ count = (data>>16) & 0x7FE;
+ if(status & RsBadCRC)
+ ether->crcs++;
+ else if(status & RsAlgnErr)
+ ether->frames++;
+ else if(status & (RsTooLong | RsTooShort))
+ ether->buffs++;
+ else {
+ len = count - Framectlsize;
+ if(len < 0)
+ panic("smc:interrupt");
+ if(ctlr->type == SMSC91C111 && !ctlr->oddworks)
+ len++;
+ else if(status & RsOddFrame)
+ len++;
+ alen = (len + 1) & ~1;
+ if(ctlr->bus32bit)
+ alen = (alen + 3) & ~3;
+ b = iallocb(alen);
+ if(b){
+ (ctlr->bus32bit? regrdatam32: regrdatam)(ctlr, b->wp, alen);
+ b->wp += len;
+ etheriq(ether, b, 1);
+ }else
+ ether->soverflows++;
+ }
+ mmucommand(ctlr, McrRemove);
+ fifo = regr(ctlr, FifoPorts);
+ }
+ intr &= ~IntRcv;
+ }
+ if(intr & IntTx){
+ /* some kind of failure */
+ fifo = regr(ctlr, FifoPorts);
+ ctlr->txerrors++;
+ if((fifo & FifoTxEmpty) == 0){
+ pkt = fifo & 0x3f;
+ pointtotxpacket(ctlr, pkt, PtrRead);
+ mmucommand(ctlr, McrFreeTx);
+ }
+ regw(ctlr, Interrupt, IntTx);
+ intr &= ~IntTx;
+ }
+ if(intr & IntTxEmpty){
+ /* acknowledge and disable TX_EMPTY */
+ regw(ctlr, Interrupt, IntTxEmpty);
+ mask &= ~IntTxEmpty;
+ intr &= ~IntTxEmpty;
+ }
+ if(intr)
+ panic("91c111: unhandled interrupts %.4ux\n", intr);
+ regw(ctlr, Interrupt, mask<<IntrMaskShift);
+ txstart(ether);
+ goto Recheck;
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+ int bank;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+ ilock(ctlr);
+ bank = llregr(ctlr, BankSelect);
+ process(ether);
+ llregw(ctlr, BankSelect, bank);
+ ctlr->bank = bank;
+ iunlock(ctlr);
+}
+
+#define MIIDELAY 5
+
+static int
+miimdi(Ctlr *ctlr, int n)
+{
+ int data, i;
+
+ /*
+ * Read n bits from the MII Management Register.
+ */
+ data = 0;
+ for(i = n - 1; i >= 0; i--){
+ if(regr(ctlr, Mgmt) & MgmtMdi)
+ data |= (1 << i);
+ microdelay(MIIDELAY);
+ regw(ctlr, Mgmt, MgmtMclk);
+ microdelay(MIIDELAY);
+ regw(ctlr, Mgmt, 0);
+ microdelay(MIIDELAY);
+ }
+
+ return data;
+}
+
+static void
+miimdo(Ctlr *ctlr, int bits, int n)
+{
+ int i, mdo;
+
+ /*
+ * Write n bits to the MII Management Register.
+ */
+ for(i = n - 1; i >= 0; i--){
+ if(bits & (1 << i))
+ mdo = MgmtMdoEn | MgmtMdo;
+ else
+ mdo = MgmtMdoEn;
+ regw(ctlr, Mgmt, mdo);
+ microdelay(MIIDELAY);
+ regw(ctlr, Mgmt, mdo | MgmtMclk);
+ microdelay(MIIDELAY);
+ regw(ctlr, Mgmt, mdo);
+ microdelay(MIIDELAY);
+ }
+}
+
+static int
+miir(Ctlr *ctlr, int regad)
+{
+ int data;
+
+ /*
+ * Preamble;
+ * ST+OP+PHYAD+REGAD;
+ * TA + 16 data bits.
+ */
+ miimdo(ctlr, 0xFFFFFFFF, 32);
+ miimdo(ctlr, 0x1800 | (ctlr->phyad << 5) | regad, 14);
+ data = miimdi(ctlr, 18);
+ regw(ctlr, Mgmt, 0);
+ microdelay(MIIDELAY);
+
+ return data & 0xFFFF;
+}
+
+static void
+miiw(Ctlr* ctlr, int regad, int data)
+{
+ /*
+ * Preamble;
+ * ST+OP+PHYAD+REGAD+TA + 16 data bits;
+ * Z.
+ */
+ miimdo(ctlr, 0xFFFFFFFF, 32);
+ data &= 0xFFFF;
+ data |= (0x05 << (5 + 5 + 2 + 16)) | (ctlr->phyad << (5 + 2 +16)) | (regad << (2 + 16)) | (0x02 << 16);
+ miimdo(ctlr, data, 32);
+ regw(ctlr, Mgmt, 0);
+ microdelay(MIIDELAY);
+}
+
+static void
+miinegostatus(Ctlr *ctlr, int *speed, int *full)
+{
+ int reg;
+
+ switch(ctlr->type){
+ case SMSC91C110:
+ reg = miir(ctlr, 25);
+ if((reg & (1<<4)) == 0)
+ break;
+ *speed = (reg & (1 << 5))? 100: 10;
+ *full = (reg & (1 << 6)) != 0;
+ return;
+ case SMSC91C111:
+ reg = miir(ctlr, 18);
+ *speed = (reg & (1 << 7))? 100: 10;
+ *full = (reg & (1 << 6)) != 0;
+ return;
+ }
+ *speed = 0;
+ *full = 0;
+}
+
+void
+dump111phyregs(Ctlr *ctlr)
+{
+ int x;
+ for(x = 0; x < 6; x++)
+ iprint("reg%d 0x%.4ux\n", x, miir(ctlr, x));
+ for(x = 16; x <= 20; x++)
+ iprint("reg%d 0x%.4ux\n", x, miir(ctlr, x));
+}
+
+static void
+miireset(Ctlr *ctlr)
+{
+ miiw(ctlr, 0, 0x8000);
+ while(miir(ctlr, 0) & 0x8000)
+ ;
+ delay(100);
+}
+
+static int
+miinegotiate(Ctlr *ctlr, int modes)
+{
+ ulong now, timeout;
+ int success;
+ int reg4;
+
+ // Taken from TRM - don't argue
+
+ miireset(ctlr);
+ miiw(ctlr, 0, 0);
+ regw(ctlr, Rpcr, 0x800 | (4 << 2));
+ delay(50);
+ reg4 = miir(ctlr, 4);
+ reg4 &= ~(0x1f << 5);
+ reg4 |= ((modes & 0x1f) << 5);
+ miiw(ctlr, 4, reg4);
+ miir(ctlr, 18); // clear the status output so we can tell which bits got set...
+ miiw(ctlr, 0, 0x3300);
+ now = timer_start();
+ timeout = ms2tmr(3000);
+ success = 0;
+ while(!success && (timer_start() - now) < timeout){
+ ushort status;
+ status = miir(ctlr, 1);
+ if(status & (1 << 5))
+ success = 1;
+ if(status & (1 << 4)){
+ success = 0;
+ miiw(ctlr, 0, 0x3300);
+ }
+ }
+ return success;
+}
+
+static int
+ether91c111reset(Ether* ether)
+{
+ int i;
+ char *p;
+ uchar ea[Eaddrlen];
+ Ctlr *ctlr;
+ ushort rev;
+
+ if(ether->ctlr == nil){
+ ether->ctlr = malloc(sizeof(Ctlr));
+ if(ether->ctlr == nil)
+ return -1;
+ }
+
+ ctlr = ether->ctlr;
+ ctlr->bank = -1;
+
+ /*
+ * do architecture dependent intialisation
+ */
+ adinit(ether);
+
+ regw(ctlr, Rcr, RcrSoftRst);
+ regw(ctlr, Config, CfgEphPowerEn|CfgNoWait|Cfg16Bit);
+ delay(4*20); // rkw - (750us for eeprom alone)4x just to be ultra conservative 10 for linux.
+ regw(ctlr, Rcr, 0); // rkw - now remove reset and let the sig's fly.
+ regw(ctlr, Tcr, TcrSwfdup);
+
+ regw(ctlr, Control, CtlAutoRelease | CtlTeEnable);
+ mmucommand(ctlr, McrReset); // rkw - reset the mmu
+ delay(5);
+
+ /*
+ * Identify the chip by reading...
+ * 1) the bank select register - the top byte will be 0x33
+ * 2) changing the bank to see if it reads back appropriately
+ * 3) check revision register for code 9
+ */
+ if((llregr(ctlr, BankSelect) >> 8) != 0x33){
+ gopanic:
+ free(ctlr);
+ return -1;
+ }
+
+ llregw(ctlr, BankSelect, 0xfffb);
+ if((llregr(ctlr, BankSelect) & 0xff07) != 0x3303)
+ goto gopanic;
+
+ rev = regr(ctlr, Revision);
+
+ if((rev >> 8) != 0x33)
+ goto gopanic;
+
+ rev &= 0xff;
+ switch(rev){
+ case 0x40:
+ /* 91c96 */
+ ctlr->type = SMSC91C96;
+ ctlr->oddworks = 1;
+ break;
+ case 0x90:
+ ctlr->type = SMSC91C11x;
+ ctlr->hasmii = 1;
+ /* 91c110/9c111 */
+ /* 91c111s are supposed to be revision one, but it's not the case */
+ // See man page 112, revision history. rev not incremented till 08/01
+ ctlr->oddworks = 0; // dont know if it works at this point
+ break;
+ case 0x91:
+ ctlr->type = SMSC91C111;
+ ctlr->hasmii = 1;
+ ctlr->oddworks = 1;
+ break;
+ default:
+ iprint("ether91c111: chip 0x%.1ux detected\n", rev);
+ goto gopanic;
+ }
+
+ memset(ea, 0, sizeof(ea));
+ if(memcmp(ether->ea, ea, Eaddrlen) == 0)
+ panic("ethernet address not set");
+#ifdef YYY
+ if((rev == 0x90) || (rev == 0x91)) // assuming no eeprom setup for these
+ panic("ethernet address not set in environment");
+ for(i = 0; i < Eaddrlen; i += 2){
+ ushort w;
+ w = regr(ctlr, Iaddr0_1 + i);
+ iprint("0x%.4ux\n", w);
+ ea[i] = w;
+ ea[i + 1] = w >> 8;
+ }
+ }else{
+ for(i = 0; i < 6; i++){
+ char buf[3];
+ buf[0] = p[i * 2];
+ buf[1] = p[i * 2 + 1];
+ buf[2] = 0;
+ ea[i] = strtol(buf, 0, 16);
+ }
+ }
+ memmove(ether->ea, ea, Eaddrlen);
+#endif
+
+ /*
+ * set the local address
+ */
+ for(i=0; i<Eaddrlen; i+=2)
+ regw(ctlr, Iaddr0_1 + i, ether->ea[i] | (ether->ea[i+1] << 8));
+
+ /*
+ * initialise some registers
+ */
+ regw(ctlr, Rcr, RcrRxEn | RcrAbortEnb | RcrStripCRC); // strip can now be used again
+
+ if(rev == 0x90){ // its either a 110 or a 111 rev A at this point
+ int reg2, reg3;
+ /*
+ * how to tell the difference?
+ * the standard MII dev
+ */
+ ctlr->phyad = PHYMIIADDR_91C110;
+ ctlr->type = SMSC91C110;
+ ctlr->oddworks = 1; // assume a 110
+ reg2 = miir(ctlr, 2); // check if a 111 RevA
+ if(reg2 <= 0){
+ ctlr->phyad = PHYMIIADDR_91C111;
+ ctlr->type = SMSC91C111;
+ reg2 = miir(ctlr, 2);
+ ctlr->oddworks = 0; // RevA
+ }
+ if(reg2 > 0){
+ reg3 = miir(ctlr, 3);
+ iprint("reg2 0x%.4ux reg3 0x%.4ux\n", reg2, reg3);
+ }
+ else
+ panic("ether91c111: can't find phy on MII\n");
+ }
+
+ if(ctlr->type == SMSC91C110)
+ regor(ctlr, Config, CfgMiiSelect);
+ if(rev == 0x40){
+ regor(ctlr, Config, CfgSetSqlch);
+ regclear(ctlr, Config, CfgAuiSelect);
+ regor(ctlr, Config, Cfg16Bit);
+ }
+
+ if(ctlr->type == SMSC91C111){
+ int modes;
+ char *ethermodes;
+
+ miiw(ctlr, 0, 0x1000); /* clear MII_DIS and enable AUTO_NEG */
+// miiw(ctlr, 16, miir(ctlr, 16) | 0x8000);
+ // Rpcr set in INIT.
+ ethermodes=nil; /* was getconf("ethermodes"); */
+ if(ethermodes == nil)
+ modes = 0xf;
+ else {
+ char *s;
+ char *args[10];
+ int nargs;
+ int x;
+
+ s = strdup(ethermodes);
+ if(s == nil)
+ panic("ether91c111reset: no memory for ethermodes");
+ nargs = getfields(s, args, nelem(args), 1, ",");
+ modes = 0;
+ for(x = 0; x < nargs; x++){
+ if(cistrcmp(args[x], "10HD") == 0)
+ modes |= 1;
+ else if(cistrcmp(args[x], "10FD") == 0)
+ modes |= 2;
+ else if(cistrcmp(args[x], "100HD") == 0)
+ modes |= 4;
+ else if(cistrcmp(args[x], "100FD") == 0)
+ modes |= 8;
+ }
+ free(s);
+ }
+ if(!miinegotiate(ctlr, modes)){
+ iprint("ether91c111: negotiation timed out\n");
+ return -1;
+ }
+ }
+
+ if(ctlr->hasmii)
+ miinegostatus(ctlr, &ether->mbps, &ether->fullduplex);
+ else if(regr(ctlr, Eph) & EphLinkOk){
+ ether->mbps = 10;
+ ether->fullduplex = 0;
+ }
+ else {
+ ether->mbps = 0;
+ ether->fullduplex = 0;
+ }
+
+ if(ether->fullduplex && ctlr->type == SMSC91C110){
+ // application note 79
+ regor(ctlr, Tcr, TcrFduplx);
+ // application note 85
+ adsetfd(ctlr);
+ }
+
+ iprint("91c111 enabled: %dmbps %s\n", ether->mbps, ether->fullduplex ? "FDX" : "HDX");
+ if(rev == 0x40){
+ iprint("EcsrEcor 0x%.4ux\n", regr(ctlr, EcsrEcor));
+ regor(ctlr, EcsrEcor, 1);
+ }
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ ether->attach = attach;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt;
+ ether->ifstat = ifstat;
+
+ ether->arg = ether;
+ ether->promiscuous = promiscuous;
+
+ return 0;
+}
+
+void
+ether91c111link(void)
+{
+ addethercard("91c111", ether91c111reset);
+}
diff --git a/os/cerf250/fns.h b/os/cerf250/fns.h
new file mode 100644
index 00000000..7b798ecc
--- /dev/null
+++ b/os/cerf250/fns.h
@@ -0,0 +1,168 @@
+#include "../port/portfns.h"
+
+ulong aifinit(uchar *aifarr);
+int archaudiopower(int);
+void archaudiomute(int);
+void archaudioamp(int);
+int archaudiospeed(int, int);
+void archconfinit(void);
+void archconsole(void);
+int archflash12v(int);
+long archkprofmicrosecondspertick(void);
+void archkprofenable(int);
+void archlcdenable(int);
+void archpowerdown(void);
+void archpowerup(void);
+void archreboot(void);
+void archreset(void);
+vlong archrdtsc(void);
+ulong archrdtsc32(void);
+void archuartpower(int, int);
+void blankscreen(int);
+ulong call_apcs(ulong addr, int nargs, ...);
+ulong call_apcs0(ulong addr);
+ulong call_apcs1(ulong addr, ulong a1);
+ulong call_apcs2(ulong addr, ulong a1, ulong a2);
+ulong call_apcs3(ulong addr, ulong a1, ulong a2, ulong a3);
+void cisread(int slotno, void (*f)(int, uchar *));
+void clockcheck(void);
+void clockinit(void);
+void clockpoll(void);
+#define coherence() /* nothing to do for cache coherence for uniprocessor */
+void cursorhide(void);
+void cursorunhide(void);
+void dcflush(void*, ulong);
+void dcflushall(void);
+void dcinval(void);
+int dmaidle(Dma*);
+Dma* dmasetup(int device, void(*)(void*,ulong), void*, ulong);
+int dmastart(Dma*, void*, void*, int);
+int dmacontinue(Dma*, void*, int);
+void dmastop(Dma*);
+int dmaerror(Dma*);
+void dmafree(Dma*);
+void dmareset(void);
+void dmawait(Dma*);
+void dumplongs(char *, ulong *, int);
+void dumpregs(Ureg* ureg);
+void dumpstack(void);
+int fpiarm(Ureg*);
+void fpinit(void);
+ulong getcallerpc(void*);
+ulong getcclkcfg(void);
+ulong getcpsr(void);
+ulong getcpuid(void);
+ulong getspsr(void);
+void gotopc(ulong);
+
+void icflush(void*, ulong);
+void icflushall(void);
+void idle(void);
+void idlehands(void);
+int inb(ulong);
+int ins(ulong);
+ulong inl(ulong);
+void outb(ulong, int);
+void outs(ulong, int);
+void outl(ulong, ulong);
+void inss(ulong, void*, int);
+void outss(ulong, void*, int);
+void insb(ulong, void*, int);
+void outsb(ulong, void*, int);
+void intrdisable(int, int, void (*)(Ureg*, void*), void*, char*);
+void intrenable(int, int, void (*)(Ureg*, void*), void*, char*);
+void iofree(int);
+#define iofree(x)
+void ioinit(void);
+int iounused(int, int);
+int ioalloc(int, int, int, char*);
+#define ioalloc(a,b,c,d) 0
+int iprint(char*, ...);
+void installprof(void (*)(Ureg *, int));
+int isvalid_va(void*);
+void kbdinit(void);
+void lcd_setbacklight(int);
+void lcd_sethz(int);
+void ledset(int);
+void lights(ulong);
+void links(void);
+void* minicached(void*);
+void minidcflush(void);
+void mmuenable(ulong);
+ulong mmugetctl(void);
+ulong mmugetdac(void);
+ulong mmugetfar(void);
+ulong mmugetfsr(void);
+void mmuinit(void);
+void* mmuphysmap(ulong, ulong);
+void mmuputctl(ulong);
+void mmuputdac(ulong);
+void mmuputfsr(ulong);
+void mmuputttb(ulong);
+void mmureset(void);
+void mouseinit(void);
+void* pa2va(ulong);
+void pcmcisread(PCMslot*);
+int pcmcistuple(int, int, int, void*, int);
+PCMmap* pcmmap(int, ulong, int, int);
+void pcmunmap(int, PCMmap*);
+int pcmpin(int slot, int type);
+void pcmpower(int slotno, int on);
+int pcmpowered(int);
+void pcmreset(int);
+void pcmsetvcc(int, int);
+void pcmsetvpp(int, int);
+int pcmspecial(char *idstr, ISAConf *isa);
+void pcmspecialclose(int slotno);
+void pcmintrenable(int, void (*)(Ureg*, void*), void*);
+void powerenable(void (*)(int));
+void powerdisable(void (*)(int));
+void powerdown(void);
+void powerinit(void);
+void powersuspend(void);
+#define procsave(p)
+#define procrestore(p)
+void putcclkcfg(ulong);
+long rtctime(void);
+void screeninit(void);
+void (*screenputs)(char*, int);
+int segflush(void*, ulong);
+void setpanic(void);
+void setr13(int, void*);
+int splfhi(void);
+int splflo(void);
+void _suspendcode(void);
+void tlbinvalidate(void);
+void tlbinvalidateaddr(void*);
+void trapinit(void);
+void trapstacks(void);
+void trapspecial(int (*)(Ureg *, uint));
+void uartdebuginit(void);
+void uartinstall(void);
+int uartprint(char*, ...);
+void uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int));
+ulong va2pa(void*);
+void vectors(void);
+void vtable(void);
+#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+int wasbusy(int);
+
+#define KADDR(p) ((void *) p)
+#define PADDR(v) va2pa((void*)(v))
+
+ulong timer_start(void);
+ulong timer_ticks(ulong);
+int timer_devwait(ulong *adr, ulong mask, ulong val, int ost);
+void timer_setwatchdog(int ost);
+void timer_delay(int ost);
+ulong ms2tmr(int ms);
+int tmr2ms(ulong t);
+void delay(int ms);
+ulong us2tmr(int us);
+int tmr2us(ulong t);
+void microdelay(int us);
+
+#define archuartclock(p,rate) 14745600
+
+/* debugging */
+void xdelay(int);
diff --git a/os/cerf250/io.h b/os/cerf250/io.h
new file mode 100644
index 00000000..6a4a9b74
--- /dev/null
+++ b/os/cerf250/io.h
@@ -0,0 +1 @@
+#include "../pxa/pxaio.h"
diff --git a/os/cerf250/main.c b/os/cerf250/main.c
new file mode 100644
index 00000000..2d5c791f
--- /dev/null
+++ b/os/cerf250/main.c
@@ -0,0 +1,351 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "version.h"
+
+#define MAXCONF 32
+
+Mach *m = (Mach*)MACHADDR;
+Proc *up = 0;
+Vectorpage *page0 = (Vectorpage*)KZERO; /* doubly-mapped to AIVECADDR */
+Conf conf;
+
+extern ulong kerndate;
+extern int cflag;
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+ulong cpuidlecount;
+
+char *confname[MAXCONF];
+char *confval[MAXCONF];
+int nconf;
+
+void addconf(char *, char *);
+void eepromscan(void);
+char* getconf(char*);
+
+void
+doc(char *m)
+{
+ USED(m);
+ print("%s...\n", m);
+}
+
+void
+idoc(char *m)
+{
+ uartputs(m, strlen(m)); //xdelay(1);
+}
+
+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){
+ port = strtol(p, nil, 0);
+ baud = 38400;
+ p = getconf("baud");
+ if(p != nil){
+ baud = strtol(p, nil, 0);
+ if(baud < 38400)
+ baud = 38400;
+ }
+ uartspecial(port, baud, &kbdq, &printq, kbdcr2nl);
+ }
+}
+
+static char *hello = "Inferno\n";
+
+void
+main(void)
+{
+ memset(edata, 0, end-edata); /* clear the BSS */
+ memset(m, 0, sizeof(Mach)); /* clear the mach struct */
+ conf.nmach = 1;
+ archreset();
+ idoc(hello);
+if(0){
+ putcclkcfg(0); /* leave turbo mode */
+ COREREG->cccr = (COREREG->cccr & ~(7<<7)) | (4<<7); /* turbo mode multiplier=2 */
+ putcclkcfg(1); /* enter turbo mode */
+}
+ quotefmtinstall();
+ idoc("confinit...\n");
+ confinit();
+ idoc("xinit...\n");
+ xinit();
+ idoc("mmuinit...\n");
+ mmuinit();
+ poolsizeinit();
+ poolinit();
+ idoc("trapinit...\n");
+ trapinit();
+// dmareset();
+ idoc("printinit...\n");
+ printinit();
+ idoc("uartinstall...\n");
+ uartinstall();
+ eepromscan();
+ doc("clockinit");
+ clockinit();
+ doc("screeninit");
+// screeninit();
+ doc("procinit");
+ procinit();
+// cpuidprint();
+ doc("links");
+ links();
+ doc("chandevreset");
+ chandevreset();
+
+ eve = strdup("inferno");
+
+ serialconsole();
+ kbdinit();
+
+ print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid());
+ print("\nInferno %s\n", VERSION);
+ print("Vita Nuova\n");
+ print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+print("pmcr=%8.8lux pwer=%8.8lux prer=%8.8lux pfer=%8.8lux pedr=%8.8lux pcfr=%8.8lux rcsr=%8.8lux\n",
+ PMGRREG->pmcr, PMGRREG->pwer, PMGRREG->prer, PMGRREG->pfer, PMGRREG->pedr, PMGRREG->pcfr,
+ PMGRREG->rcsr);
+print("cccr=%8.8lux cken=%8.8lux oscc=%8.8lux\n", COREREG->cccr, COREREG->cken, COREREG->oscc);
+print("msc0=%8.8lux mcs1=%8.8lux mcs2=%8.8lux\n", MEMCFGREG->msc0, MEMCFGREG->msc1, MEMCFGREG->msc2);
+print("clkcfg=%8.8lux\n", getcclkcfg());
+
+ userinit();
+ schedinit();
+}
+
+void
+reboot(void)
+{
+ exit(0);
+}
+
+void
+halt(void)
+{
+ spllo();
+ print("cpu halted\n");
+ for(;;){
+ /* nothing to do */
+ }
+}
+
+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)
+{
+ ulong base;
+
+ archconfinit();
+
+ base = PGROUND((ulong)end);
+ conf.base0 = base;
+
+ conf.base1 = 0;
+ conf.npage1 = 0;
+
+ conf.npage0 = (conf.topofmem - base)/BY2PG;
+
+ conf.npage = conf.npage0 + conf.npage1;
+ conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG;
+
+ conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+ conf.nmach = 1;
+
+}
+
+void
+init0(void)
+{
+ Osenv *o;
+ char buf[2*KNAMELEN];
+
+ up->nerrlab = 0;
+
+ spllo();
+
+ if(waserror())
+ panic("init0 %r");
+ /*
+ * 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", "arm", 0);
+ snprint(buf, sizeof(buf), "arm %s", conffile);
+ ksetenv("terminal", buf, 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");
+
+ p->fpstate = FPINIT;
+
+ /*
+ * Kernel Stack
+ *
+ * N.B. The -12 for the stack pointer is important.
+ * 4 bytes for gotolabel's return PC
+ */
+ p->sched.pc = (ulong)init0;
+ p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+ ready(p);
+}
+
+void
+exit(int inpanic)
+{
+ up = 0;
+
+ /* Shutdown running devices */
+ chandevshutdown();
+
+ if(inpanic && 0){
+ print("Hit the reset button\n");
+ for(;;)
+ clockpoll();
+ }
+ archreboot();
+}
+
+static void
+linkproc(void)
+{
+ spllo();
+ if (waserror())
+ print("error() underflow: %r\n");
+ else
+ (*up->kpfun)(up->arg);
+ pexit("end proc", 1);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+ p->sched.pc = (ulong)linkproc;
+ p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+ p->kpfun = func;
+ p->arg = arg;
+}
+
+void
+idlehands(void)
+{
+ cpuidlecount++;
+ INTRREG->iccr = 1; /* only unmasked interrupts will stop idle mode */
+ idle();
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+ return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+ return 0;
+}
+
+void
+fpinit(void)
+{
+}
+
+void
+FPsave(void*)
+{
+}
+
+void
+FPrestore(void*)
+{
+}
diff --git a/os/cerf250/mem.h b/os/cerf250/mem.h
new file mode 100644
index 00000000..625232ab
--- /dev/null
+++ b/os/cerf250/mem.h
@@ -0,0 +1,174 @@
+/*
+ * 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 BIT(n) (1<<n)
+#define BITS(a,b) ((1<<(b+1))-(1<<a))
+
+#define MAXMACH 1 /* max # cpus system can run */
+
+/*
+ * 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 */
+
+/*
+ * More accurate time
+ */
+#define CLOCKFREQ 3686400
+#define MS2TMR(t) ((ulong)(((uvlong)(t)*CLOCKFREQ)/1000))
+#define US2TMR(t) ((ulong)(((uvlong)(t)*CLOCKFREQ)/1000000))
+
+/*
+ * Address spaces
+ * nearly everything maps 1-1 with physical addresses
+ * 0 to 1Mb is not mapped
+ * cache strategy varies as needed (see mmu.c)
+ */
+
+#define KZERO 0xA0000000
+#define MACHADDR (KZERO+0x00001000)
+#define KTTB (KZERO+0x00004000)
+/*#define KTZERO (KZERO+0x00008010)*/
+#define KTZERO (KZERO+0x200020) /* temporary */
+#define KSTACK 8192 /* Size of kernel stack */
+#define FLASHMEM 0x50000000 /* map flash at phys 0 to otherwise unused virtual space */
+#define FLUSHMEM 0xE0000000 /* virtual address reserved for cache flushing */
+#define DCFADDR FLUSHMEM /* cached and buffered for cache writeback */
+#define MCFADDR (FLUSHMEM+(1<<20)) /* cached and unbuffered for minicache writeback */
+#define UCDRAMZERO 0xA8000000 /* base of memory doubly-mapped as uncached */
+#define AIVECADDR 0xFFFF0000 /* alternative interrupt vector address (other is 0) */
+
+/*
+ * Physical addresses
+ */
+#define PHYSFLASH0 0x00000000 /* flash (chip select 0) */
+#define PHYSCS1 0x04000000 /* static chip select 1 */
+#define PHYSCS2 0x08000000 /* static chip select 2 */
+#define PHYSCS3 0x0C000000 /* static chip select 3 */
+#define PHYSCS4 0x10000000 /* static chip select 4 */
+#define PHYSCS5 0x18000000 /* static chip select 5 */
+#define PHYSPCMCIA0 0x20000000 /* PCMCIA socket 0 space */
+#define PHYSPCMCIA1 0x30000000 /* PCMCIA socket 1 space */
+#define PCMCIASIZE 0x10000000 /* they're both huge */
+#define PHYSREGS 0x40000000 /* memory mapped registers */
+#define PHYSDMA 0x40000000 /* DMA controller */
+#define PHYSUART0 0x40100000 /* full function UART*/
+#define PHYSUARTBT 0x40200000 /* bluetooth UART */
+#define PHYSI2C 0x40301680
+#define PHYSI2S 0x40400000 /* serial audio */
+#define PHYSAC97 0x40500000 /* AC97/PCM/modem */
+#define PHYSUDC 0x40600000 /* USB client */
+#define PHYSUART1 0x40700000 /* standard UART */
+#define PHYSICP 0x40800000
+#define PHYSRTC 0x40900000 /* real-time clock */
+#define PHYSOSTMR 0x40A00000 /* timers */
+#define PHYSPWM0 0x40B00000 /* pulse width modulator */
+#define PHYSPWM1 0x40C00000
+#define PHYSINTR 0x40D00000 /* interrupt controller */
+#define PHYSGPIO 0x40E00000 /* pins */
+#define PHYSPOWER 0x40F00000 /* power management registers */
+#define PHYSSSP 0x41000000
+#define PHYSMMC 0x41100000
+#define PHYSCORE 0x41300000 /* clocks manager */
+#define PHYSNETSSP 0x41400000 /* network SSP */
+#define PHYSUART2 0x41600000 /* hardware UART */
+#define PHYSLCD 0x44000000 /* LCD controller */
+#define PHYSMEMCFG 0x48000000 /* memory configuration */
+#define PHYSMEM0 0xA0000000
+
+/*
+ * Memory Interface Control Registers
+ */
+#define MDCNFG (PHYSMEMCFG) /* memory controller configuration */
+#define MDREFR (PHYSMEMCFG+4)
+#define MSC0 (MDREFR+4)
+#define MSC1 (MSC0+4)
+#define MSC2 (MSC1+4)
+
+#define MSCx(RRR, RDN, RDF, RBW, RT) ((((RRR)&0x7)<<13)|(((RDN)&0x1F)<<8)|(((RDF)&0x1F)<<3)|(((RBW)&1)<<2)|((RT)&3))
+
+#define CACHELINELOG 5
+#define CACHELINESZ (1<<CACHELINELOG)
+#define CACHESIZE (32*1024) /* I & D caches are the same size */
+#define MINICACHESIZE (2*1024)
+
+/*
+ * PSR
+ */
+#define PsrMusr 0x10 /* mode */
+#define PsrMfiq 0x11
+#define PsrMirq 0x12
+#define PsrMsvc 0x13
+#define PsrMabt 0x17
+#define PsrMund 0x1B
+#define PsrMsys 0x1F
+#define PsrMask 0x1F
+
+#define PsrDfiq 0x00000040 /* disable FIQ interrupts */
+#define PsrDirq 0x00000080 /* disable IRQ interrupts */
+
+#define PsrV 0x10000000 /* overflow */
+#define PsrC 0x20000000 /* carry/borrow/extend */
+#define PsrZ 0x40000000 /* zero */
+#define PsrN 0x80000000 /* negative/less than */
+
+/*
+ * Internal MMU coprocessor registers
+ */
+#define CpCPUID 0 /* R: opcode_2 is 0*/
+#define CpCacheID 0 /* R: opcode_2 is 1 */
+#define CpControl 1 /* R/W: control (opcode_2 is 0) */
+#define CpAuxctl 1 /* R/W: auxiliary control (opcode_2 is 1) */
+#define CpTTB 2 /* R/W: translation table base */
+#define CpDAC 3 /* R/W: domain access control */
+#define CpFSR 5 /* R/W: fault status */
+#define CpFAR 6 /* R/W: fault address */
+#define CpCacheCtl 7 /* W: */
+#define CpTLBops 8 /* W: TLB operations */
+#define CpCacheLk 9 /* W: cache lock down */
+#define CpPID 13 /* R/W: Process ID Virtual Mapping */
+#define CpDebug 14 /* R/W: debug registers */
+#define CpAccess 15 /* R/W: Coprocessor Access */
+
+/*
+ * Coprocessors
+ */
+#define CpMMU 15
+#define CpPWR 14
+
+/*
+ * CpControl bits
+ */
+#define CpCmmu (1<<0) /* M: MMU enable */
+#define CpCalign (1<<1) /* A: alignment fault enable */
+#define CpCDcache (1<<2) /* C: data cache on */
+#define CpCwpd (15<<3) /* W, P, D, must be one */
+#define CpCbe (1<<7) /* B: big-endian operation */
+#define CpCsystem (1<<8) /* S: system permission */
+#define CpCrom (1<<9) /* R: ROM permission */
+#define CpCbranch (1<<11) /* Z: branch target buffer enable */
+#define CpCIcache (1<<12) /* I: Instruction Cache on */
+#define CpCaltivec (1<<13) /* V: exception vector relocation */
+
+/*
+ * CpAux bits
+ */
+#define CpWBdisable (1<<1) /* globally disable write buffer coalescing */
+#define CpMDrwa (1<<4) /* mini data cache r/w allocate */
+#define CpMDwt (1<<5) /* mini data cache write through */
diff --git a/os/cerf250/mkfile b/os/cerf250/mkfile
new file mode 100644
index 00000000..2aece3f1
--- /dev/null
+++ b/os/cerf250/mkfile
@@ -0,0 +1,102 @@
+<../../mkconfig
+TKSTYLE=std
+
+#Configurable parameters
+
+CONF=cerf #default configuration
+CONFLIST=cerf
+
+SYSTARG=$OSTARG
+OBJTYPE=arm
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed
+#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
+
+#KTZERO=0xA0008020
+KTZERO=0xA0020020
+
+OBJ=\
+ l.$O\
+ clock.$O\
+ fpi.$O\
+ fpiarm.$O\
+ fpimem.$O\
+ gpio.$O\
+ main.$O\
+ mmu.$O\
+ trap.$O\
+ $CONF.root.$O\
+ $IP\
+ $DEVS\
+ $ETHERS\
+ $LINKS\
+ $PORT\
+ $MISC\
+ $OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+LIBDIRS=$LIBS
+
+HFILES=\
+ mem.h\
+ dat.h\
+ fns.h\
+ io.h\
+ ../pxa/pxaio.h\
+ ../pxa/fpi.h\
+
+CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I../pxa
+KERNDATE=`{$NDATE}
+
+default:V: i$CONF i$CONF.p9
+
+install:V: $INSTALLDIR/i$CONF
+
+i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES i$CONF.p9
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD -s -o $target -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD -o $target -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES
+
+<../port/portmkfile
+
+%.$O: ../pxa/%.c
+ $CC $CFLAGS -I. ../pxa/$stem.c
+
+%.$O: ../pxa/%.s
+ $AS -I. -I../pxa ../pxa/$stem.s
+
+../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
+devsapcm.$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: ../pxa/etherif.h ../port/netif.h
+$IP devip.$O: ../ip/ip.h
+io.h:N: ../pxa/pxaio.h
+
+dummy:V:
+
+# to be moved to libinterp
+bench.h:D: ../../module/bench.m
+ rm -f $target && limbo -a -I../../module ../../module/bench.m > $target
+benchmod.h:D: ../../module/bench.m
+ rm -f $target && limbo -t Bench -I../../module ../../module/bench.m > $target
+devbench.$O: bench.h benchmod.h
+
+devaudio.$O: devaudio.c
+ $CC $CFLAGS devaudio.c
+
+arch$CONF.$O: ../pxa/etherif.h
+
+devuart.$O: ../pxa/devuart.c uart.h
+ $CC $CFLAGS ../pxa/devuart.c
diff --git a/os/cerf250/uart.h b/os/cerf250/uart.h
new file mode 100644
index 00000000..77493daa
--- /dev/null
+++ b/os/cerf250/uart.h
@@ -0,0 +1,118 @@
+/*
+ * PXA250 specific code for its uart.
+ */
+
+#define UR(p,r) ((ulong*)(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 *p, int on)
+{
+ /* TO DO: power control */
+ if(on)
+ p->sticky[Iena] |= Uue;
+ else
+ p->sticky[Iena] &= ~Uue;
+ uartwrreg(p, Iena, 0);
+}
+
+/*
+ * 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"); /* full function */
+ intrenable(IRQ, IRQffuart, uartintrx, uart[0], "uart0");
+ uartsetup(2, (void*)PHYSUART2, 0, "eia2"); /* hardware uart */
+ intrenable(IRQ, IRQhwuart, uartintrx, uart[1], "uart2");
+ 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
+uartdebuginit(void)
+{
+ ulong *p;
+
+ p = (ulong*)PHYSUART0;
+ p[Iena] = Uue;
+ p[Format] = Dra;
+ p[Dmsb] = 0;
+ p[Dlsb] = 24;
+ p[Format] = Bits8;
+}
+
+void
+uartputc(int c)
+{
+ ulong *p;
+
+ if(c == 0)
+ return;
+ p = (ulong*)PHYSUART0;
+ while((UR(p,Lstat) & Outready) == 0){
+ ;
+ }
+ UR(p,Data) = c;
+ 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)
+{
+}