summaryrefslogtreecommitdiff
path: root/os/cerf1110
diff options
context:
space:
mode:
Diffstat (limited to 'os/cerf1110')
-rwxr-xr-xos/cerf1110/Mk8
-rw-r--r--os/cerf1110/NOTICE2
-rw-r--r--os/cerf1110/README22
-rw-r--r--os/cerf1110/archcerf.c177
-rw-r--r--os/cerf1110/cerf159
-rw-r--r--os/cerf1110/dat.h135
-rw-r--r--os/cerf1110/devata.c1200
-rw-r--r--os/cerf1110/devcerf.c128
-rw-r--r--os/cerf1110/ether8900.c702
-rw-r--r--os/cerf1110/fns.h179
-rw-r--r--os/cerf1110/io.h1
-rw-r--r--os/cerf1110/main.c255
-rw-r--r--os/cerf1110/mem.h200
-rw-r--r--os/cerf1110/mkfile101
14 files changed, 3269 insertions, 0 deletions
diff --git a/os/cerf1110/Mk b/os/cerf1110/Mk
new file mode 100755
index 00000000..51d69a44
--- /dev/null
+++ b/os/cerf1110/Mk
@@ -0,0 +1,8 @@
+#!/bin/rc
+rfork ne
+ROOT=/usr/inferno
+fn cd
+NPROC=3
+path=(/usr/inferno/Plan9/$cputype/bin $path)
+#bind /usr/inferno/mkconfig.dist /usr/inferno/mkconfig
+exec mk $*
diff --git a/os/cerf1110/NOTICE b/os/cerf1110/NOTICE
new file mode 100644
index 00000000..537233a4
--- /dev/null
+++ b/os/cerf1110/NOTICE
@@ -0,0 +1,2 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved.
+Cerfcube 1110 Inferno port Copyright © 2000,2003 Vita Nuova Holdings Limited. All rights reserved.
diff --git a/os/cerf1110/README b/os/cerf1110/README
new file mode 100644
index 00000000..5354030c
--- /dev/null
+++ b/os/cerf1110/README
@@ -0,0 +1,22 @@
+Booting Inferno on a Cerf cube
+
+Build the /usr/inferno/os/cerf1110 kernel into /usr/inferno/os/cerf1110/icerf:
+ mk
+It uses common SA1110 code in ../sa1110, 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 `Waiting for RTC to stabilize'
+ hit a key.
+
+2. type
+ tftp /usr/inferno/os/cerf1110/icerf c0008000
+ with appropriate substitution for file name.
+
+3. on success
+ boot c0008000 0 0
+
+it should run.
diff --git a/os/cerf1110/archcerf.c b/os/cerf1110/archcerf.c
new file mode 100644
index 00000000..86eb902e
--- /dev/null
+++ b/os/cerf1110/archcerf.c
@@ -0,0 +1,177 @@
+/*
+ * Intrinsyc Cerf cube
+ */
+#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 {
+ /* Cerf GPIO assignment */
+ LED0 = 1<<0, /* active high */
+ LED1 = 1<<1,
+ LED2 = 1<<2,
+ LED3 = 1<<3,
+ /* 4 to 15 appear on J2 */
+ CFBVD1 = 1<<20,
+ CFBVD2 = 1<<19,
+ CFReset = 1<<21, /* active low for IDE mode; active high for IO or memory mode */
+ CFRdypin = 22,
+ CFRdy = 1<<CFRdypin, /* RDY/nBSY */
+ CFnCDxpin = 23,
+ CFnCDx = 1<<CFnCDxpin, /* low => card inserted */
+ EnableRS232In = 1<<24,
+ EnableRS232Out = 1<<25,
+ /* CS8900 interrupt on 26, active high */
+ /* CS8900 nHWSLEEP on 27 */
+};
+
+void
+archreset(void)
+{
+ GpioReg *g = GPIOREG;
+
+ g->grer = 0;
+ g->gfer = 0;
+ g->gedr = g->gedr;
+ g->gpdr = 0;
+
+ g->gpdr = EnableRS232In | EnableRS232Out | CFReset;
+ g->gpsr = EnableRS232In | EnableRS232Out;
+
+ GPCLKREG->gpclkr0 |= 1; /* SUS=1 for uart on serial 1 */
+}
+
+void
+archconfinit(void)
+{
+ int w;
+
+ conf.topofmem = 0xC0000000+32*MB;
+ w = PMGRREG->ppcr & 0x1f;
+ m->cpuhz = CLOCKFREQ*(w*4+16);
+
+ conf.useminicache = 1;
+ conf.portrait = 1; /* should take from param flash or allow dynamic change */
+}
+
+void
+archconsole(void)
+{
+ uartspecial(0, 38400, 'n', &kbdq, &printq, kbdcr2nl);
+}
+
+void
+archuartpower(int, int)
+{
+}
+
+void
+kbdinit(void)
+{
+}
+
+void
+archreboot(void)
+{
+ dcflushall();
+ 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 = 2;
+ return 0;
+}
+
+/*
+ * 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;
+ switch(type){
+ case PCMready:
+ return CFRdypin;
+ case PCMeject:
+ return CFnCDxpin;
+ case PCMstschng:
+ return -1;
+ }
+}
+
+void
+pcmsetvpp(int slot, int vpp)
+{
+ USED(slot, vpp);
+}
+
+/*
+ * 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, "CS8900");
+ ether->nopt = 0;
+ ether->irq = 26; /* GPIO */
+ ether->itype = BusGPIOrising;
+ return 1;
+}
diff --git a/os/cerf1110/cerf b/os/cerf1110/cerf
new file mode 100644
index 00000000..24002405
--- /dev/null
+++ b/os/cerf1110/cerf
@@ -0,0 +1,159 @@
+dev
+ root
+ cons archcerf
+ env
+ gpio
+ mnt
+ pipe
+ prog
+ rtc
+ 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 ethermedium
+
+ cerf
+# kprof
+
+ip
+ il
+ tcp
+ udp
+# rudp
+# igmp
+ ipifc
+ icmp
+ icmp6
+# ipmux
+
+link
+ flashcfi16
+ ether8900
+
+lib
+ interp
+# tk
+# draw
+# memlayer
+# memdraw
+ keyring
+ sec
+ mp
+ math
+ 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 kernel_pool_pcnt = 10;
+ int main_pool_pcnt = 40;
+ int heap_pool_pcnt = 40;
+ int image_pool_pcnt = 0;
+ int cflag = 0; /* for JIT */
+
+ int consoleprint = 1;
+ int redirectconsole = 1;
+ char debug_keys = 1;
+ int panicreset = 0;
+ void screeninit(void){}
+
+init
+ cerfinit
+
+root
+ /chan /
+ /dev /
+ /dis
+ /env /
+ /fd /
+ /net /
+ /net.alt /
+ /nvfs /
+ /prog /
+ /dis/lib
+ /dis/disk
+ /osinit.dis
+
+# 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/cerf1110/dat.h b/os/cerf1110/dat.h
new file mode 100644
index 00000000..553c0cb0
--- /dev/null
+++ b/os/cerf1110/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/cerf1110/devata.c b/os/cerf1110/devata.c
new file mode 100644
index 00000000..bca9cda3
--- /dev/null
+++ b/os/cerf1110/devata.c
@@ -0,0 +1,1200 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define DPRINT if(0)iprint
+
+typedef struct Drive Drive;
+typedef struct Ident Ident;
+typedef struct Controller Controller;
+typedef struct Partition Partition;
+typedef struct Repl Repl;
+
+enum
+{
+ /* ports */
+ Pbase= 0x1F0,
+ Pdata= 0, /* data port (16 bits) */
+ Perror= 1, /* error port (read) */
+ Pprecomp= 1, /* buffer mode port (write) */
+ Pcount= 2, /* sector count port */
+ Psector= 3, /* sector number port */
+ Pcyllsb= 4, /* least significant byte cylinder # */
+ Pcylmsb= 5, /* most significant byte cylinder # */
+ Pdh= 6, /* drive/head port */
+ Pstatus= 7, /* status port (read) */
+ Sbusy= (1<<7),
+ Sready= (1<<6),
+ Sdrq= (1<<3),
+ Serr= (1<<0),
+ Pcmd= 7, /* cmd port (write) */
+
+ /* commands */
+ Crecal= 0x10,
+ Cread= 0x20,
+ Cwrite= 0x30,
+ Cident= 0xEC,
+ Cident2= 0xFF, /* pseudo command for post Cident interrupt */
+ Csetbuf= 0xEF,
+ Cinitparam= 0x91,
+
+ /* conner specific commands */
+ Cstandby= 0xE2,
+ Cidle= 0xE1,
+ Cpowerdown= 0xE3,
+
+ /* disk states */
+ Sspinning,
+ Sstandby,
+ Sidle,
+ Spowerdown,
+
+ /* something we have to or into the drive/head reg */
+ DHmagic= 0xA0,
+
+ /* file types */
+ Qdir= 0,
+ Qfile,
+
+ Maxxfer= BY2PG, /* maximum transfer size/cmd */
+ Npart= 8+2, /* 8 sub partitions, disk, and partition */
+ Nrepl= 64, /* maximum replacement blocks */
+};
+#define PART(x) (((x)>>1)&0xF)
+#define DRIVE(x) (((x)>>5)&0x7)
+#define MKQID(d,p) (((d)<<5) | ((p)<<1) | Qfile)
+
+struct Partition
+{
+ ulong start;
+ ulong end;
+ char name[KNAMELEN+1];
+};
+
+struct Repl
+{
+ Partition *p;
+ int nrepl;
+ ulong blk[Nrepl];
+};
+
+#define PARTMAGIC "plan9 partitions"
+#define REPLMAGIC "block replacements"
+
+/*
+ * an ata drive
+ */
+struct Drive
+{
+ QLock;
+
+ Controller *cp;
+ int drive;
+ int confused; /* needs to be recalibrated (or worse) */
+ int online;
+ int npart; /* number of real partitions */
+ Partition p[Npart];
+ Repl repl;
+ ulong usetime;
+ int state;
+ char vol[KNAMELEN];
+
+ ulong cap; /* total bytes */
+ int bytes; /* bytes/sector */
+ int sectors; /* sectors/track */
+ int heads; /* heads/cyl */
+ long cyl; /* cylinders/drive */
+
+ char lba; /* true if drive has logical block addressing */
+ char multi; /* non-zero if drive does multiple block xfers */
+};
+
+/*
+ * a controller for 2 drives
+ */
+struct Controller
+{
+ QLock; /* exclusive access to the controller */
+ ISAConf; /* interface to pcmspecial */
+
+ Lock reglock; /* exclusive access to the registers */
+
+ int confused; /* needs to be recalibrated (or worse) */
+ ulong pbase; /* base port (copied from ISAConf) */
+
+ /*
+ * current operation
+ */
+ int cmd; /* current command */
+ int lastcmd; /* debugging info */
+ Rendez r; /* wait here for command termination */
+ char *buf; /* xfer buffer */
+ int nsecs; /* length of transfer (sectors) */
+ int sofar; /* sectors transferred so far */
+ int status;
+ int error;
+ Drive *dp; /* drive being accessed */
+};
+
+Controller *atac;
+Drive *ata;
+static char* ataerr;
+static int nhard;
+static int spindowntime;
+
+static void ataintr(Ureg*, void*);
+static long ataxfer(Drive*, Partition*, int, long, long, char*);
+static void ataident(Drive*);
+static void atasetbuf(Drive*, int);
+static void ataparams(Drive*);
+static void atapart(Drive*);
+static int ataprobe(Drive*, int, int, int);
+
+static int
+atagen(Chan *c, char*, Dirtab*, int, int s, Dir *dirp)
+{
+ Qid qid;
+ int drive;
+ Drive *dp;
+ Partition *pp;
+ ulong l;
+
+ if(s == DEVDOTDOT){
+ mkqid(&qid, 0, 0, QTDIR);
+ sprint(up->genbuf, "#%C", devtab[c->type]->dc);
+ devdir(c, qid, up->genbuf, 0, eve, 0555, dirp);
+ return 1;
+ }
+ qid.vers = 0;
+ qid.type = QTFILE;
+ drive = s/Npart;
+ s = s % Npart;
+ if(drive >= nhard)
+ return -1;
+ dp = &ata[drive];
+
+ if(dp->online == 0 || s >= dp->npart)
+ return 0;
+
+ pp = &dp->p[s];
+ sprint(up->genbuf, "%s%s", dp->vol, pp->name);
+ qid.path = MKQID(drive, s);
+ l = (pp->end - pp->start) * dp->bytes;
+ devdir(c, qid, up->genbuf, l, eve, 0660, dirp);
+ return 1;
+}
+
+static void
+atainit(void)
+{
+ Drive *dp;
+ Controller *cp;
+ uchar equip;
+ int pcmslot;
+
+ if (atac)
+ return; /* already done */
+
+ equip = 0x10; /* hard coded */
+
+ cp = malloc(sizeof(*cp));
+ if (!cp)
+ error(Enomem);
+
+ cp->port = Pbase;
+ cp->irq = 14;
+ cp->nopt = 1;
+ cp->opt[0] = "index=1";
+
+ if((pcmslot = pcmspecial("ATA FLASH", cp)) < 0) {
+ DPRINT("No ATA card\n");
+ free(cp);
+ ataerr = Enoifc;
+ return;
+ }
+ ata = malloc(2 * sizeof(*ata));
+ if(ata == nil) {
+ pcmspecialclose(pcmslot);
+ free(cp);
+ error(Enomem);
+ }
+
+ atac = cp;
+ cp->buf = 0;
+ cp->lastcmd = cp->cmd;
+ cp->cmd = 0;
+ cp->pbase = cp->port;
+ intrenable(cp->irq, ataintr, cp, cp->itype, "ata");
+
+ dp = ata;
+ if(equip & 0xf0){
+ dp->drive = 0;
+ dp->online = 0;
+ dp->cp = cp;
+ dp++;
+ }
+ if((equip & 0x0f)){
+ dp->drive = 1;
+ dp->online = 0;
+ dp->cp = cp;
+ dp++;
+ }
+ nhard = dp - ata;
+
+ spindowntime = 1;
+}
+
+
+/*
+ * Get the characteristics of each drive. Mark unresponsive ones
+ * off line.
+ */
+static Chan*
+ataattach(char *spec)
+{
+ Drive *dp;
+
+ atainit();
+ if (!ata)
+ error(ataerr ? ataerr : Enoifc);
+ for(dp = ata; dp < &ata[nhard]; dp++){
+ if(waserror()){
+ dp->online = 0;
+ qunlock(dp);
+ continue;
+ }
+ qlock(dp);
+ if(!dp->online){
+ /*
+ * Make sure ataclock() doesn't
+ * interfere.
+ */
+ dp->usetime = m->ticks;
+ ataparams(dp);
+ dp->online = 1;
+ atasetbuf(dp, 1);
+ }
+
+ /*
+ * read Plan 9 partition table
+ */
+ atapart(dp);
+ qunlock(dp);
+ poperror();
+ }
+ return devattach('H', spec);
+}
+
+static Walkqid*
+atawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, 0, 0, atagen);
+}
+
+static int
+atastat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, 0, 0, atagen);
+}
+
+static Chan*
+ataopen(Chan *c, int omode)
+{
+ return devopen(c, omode, 0, 0, atagen);
+}
+
+static void
+ataclose(Chan *c)
+{
+ Drive *d;
+ Partition *p;
+
+ if(c->mode != OWRITE && c->mode != ORDWR)
+ return;
+
+ d = &ata[DRIVE(c->qid.path)];
+ p = &d->p[PART(c->qid.path)];
+ if(strcmp(p->name, "partition") != 0)
+ return;
+
+ if(waserror()){
+ qunlock(d);
+ nexterror();
+ }
+ qlock(d);
+ atapart(d);
+ qunlock(d);
+ poperror();
+}
+
+static long
+ataread(Chan *c, void *a, long n, vlong offset)
+{
+ Drive *dp;
+ long rv, i;
+ int skip;
+ uchar *aa = a;
+ Partition *pp;
+ char *buf;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, atagen);
+
+ buf = smalloc(Maxxfer);
+ if(waserror()){
+ free(buf);
+ nexterror();
+ }
+
+ dp = &ata[DRIVE(c->qid.path)];
+ pp = &dp->p[PART(c->qid.path)];
+
+ skip = offset % dp->bytes;
+ for(rv = 0; rv < n; rv += i){
+ i = ataxfer(dp, pp, Cread, offset+rv-skip, n-rv+skip, buf);
+ if(i == 0)
+ break;
+ i -= skip;
+ if(i > n - rv)
+ i = n - rv;
+ memmove(aa+rv, buf + skip, i);
+ skip = 0;
+ }
+
+ free(buf);
+ poperror();
+
+ return rv;
+}
+
+static long
+atawrite(Chan *c, void *a, long n, vlong offset)
+{
+ Drive *dp;
+ long rv, i, partial;
+ uchar *aa = a;
+ Partition *pp;
+ char *buf;
+
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+
+ dp = &ata[DRIVE(c->qid.path)];
+ pp = &dp->p[PART(c->qid.path)];
+ buf = smalloc(Maxxfer);
+ if(waserror()){
+ free(buf);
+ nexterror();
+ }
+
+ /*
+ * if not starting on a sector boundary,
+ * read in the first sector before writing
+ * it out.
+ */
+ partial = offset % dp->bytes;
+ if(partial){
+ ataxfer(dp, pp, Cread, offset-partial, dp->bytes, buf);
+ if(partial+n > dp->bytes)
+ rv = dp->bytes - partial;
+ else
+ rv = n;
+ memmove(buf+partial, aa, rv);
+ ataxfer(dp, pp, Cwrite, offset-partial, dp->bytes, buf);
+ } else
+ rv = 0;
+
+ /*
+ * write out the full sectors
+ */
+ partial = (n - rv) % dp->bytes;
+ n -= partial;
+ for(; rv < n; rv += i){
+ i = n - rv;
+ if(i > Maxxfer)
+ i = Maxxfer;
+ memmove(buf, aa+rv, i);
+ i = ataxfer(dp, pp, Cwrite, offset+rv, i, buf);
+ if(i == 0)
+ break;
+ }
+
+ /*
+ * if not ending on a sector boundary,
+ * read in the last sector before writing
+ * it out.
+ */
+ if(partial){
+ ataxfer(dp, pp, Cread, offset+rv, dp->bytes, buf);
+ memmove(buf, aa+rv, partial);
+ ataxfer(dp, pp, Cwrite, offset+rv, dp->bytes, buf);
+ rv += partial;
+ }
+
+ free(buf);
+ poperror();
+
+ return rv;
+}
+
+/*
+ * did an interrupt happen?
+ */
+static int
+cmddone(void *a)
+{
+ Controller *cp = a;
+
+ return cp->cmd == 0;
+}
+
+/*
+ * Wait for the controller to be ready to accept a command.
+ * This is protected from interference by ataclock() by
+ * setting dp->usetime before it is called.
+ */
+static void
+cmdreadywait(Drive *dp)
+{
+ long start;
+ int period;
+ Controller *cp = dp->cp;
+
+ /* give it 2 seconds to spin down and up */
+ if(dp->state == Sspinning)
+ period = 10;
+ else
+ period = 2000;
+
+ start = m->ticks;
+ while((inb(cp->pbase+Pstatus) & (Sready|Sbusy)) != Sready)
+ if(TK2MS(m->ticks - start) > period){
+ DPRINT("cmdreadywait failed\n");
+ error(Eio);
+ }
+}
+
+static void
+atarepl(Drive *dp, long bblk)
+{
+ int i;
+
+ if(dp->repl.p == 0)
+ return;
+ for(i = 0; i < dp->repl.nrepl; i++){
+ if(dp->repl.blk[i] == bblk)
+ DPRINT("found bblk %ld at offset %d\n", bblk, i);
+ }
+}
+
+static void
+atasleep(Controller *cp, int ms)
+{
+ tsleep(&cp->r, cmddone, cp, ms);
+ if(cp->cmd && cp->cmd != Cident2){
+ DPRINT("ata: cmd 0x%uX timeout, status=%ux\n",
+ cp->cmd, inb(cp->pbase+Pstatus));
+ error("ata drive timeout");
+ }
+}
+
+/*
+ * transfer a number of sectors. ataintr will perform all the iterative
+ * parts.
+ */
+static long
+ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf)
+{
+ Controller *cp;
+ long lblk;
+ int cyl, sec, head;
+ int loop, stat;
+
+ if(dp->online == 0)
+ error(Eio);
+
+ /*
+ * cut transfer size down to disk buffer size
+ */
+ start = start / dp->bytes;
+ if(len > Maxxfer)
+ len = Maxxfer;
+ len = (len + dp->bytes - 1) / dp->bytes;
+ if(len == 0)
+ return 0;
+
+ /*
+ * calculate physical address
+ */
+ lblk = start + pp->start;
+ if(lblk >= pp->end)
+ return 0;
+ if(lblk+len > pp->end)
+ len = pp->end - lblk;
+ if(dp->lba){
+ sec = lblk & 0xff;
+ cyl = (lblk>>8) & 0xffff;
+ head = (lblk>>24) & 0xf;
+ } else {
+ cyl = lblk/(dp->sectors*dp->heads);
+ sec = (lblk % dp->sectors) + 1;
+ head = ((lblk/dp->sectors) % dp->heads);
+ }
+
+ DPRINT("<%s %ld>", (cmd == Cwrite) ? "W" : "R", lblk);
+ cp = dp->cp;
+ qlock(cp);
+ if(waserror()){
+ cp->buf = 0;
+ qunlock(cp);
+ nexterror();
+ }
+
+ /*
+ * Make sure hardclock() doesn't
+ * interfere.
+ */
+ dp->usetime = m->ticks;
+ cmdreadywait(dp);
+
+ ilock(&cp->reglock);
+ cp->sofar = 0;
+ cp->buf = buf;
+ cp->nsecs = len;
+ cp->cmd = cmd;
+ cp->dp = dp;
+ cp->status = 0;
+
+ outb(cp->pbase+Pcount, cp->nsecs);
+ outb(cp->pbase+Psector, sec);
+ outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | (dp->lba<<6) | head);
+ outb(cp->pbase+Pcyllsb, cyl);
+ outb(cp->pbase+Pcylmsb, cyl>>8);
+ outb(cp->pbase+Pcmd, cmd);
+
+ if(cmd == Cwrite){
+ loop = 0;
+ microdelay(1);
+ while((stat = inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0)
+ if(++loop > 10000)
+ panic("ataxfer");
+ outss(cp->pbase+Pdata, cp->buf, dp->bytes/2);
+ } else
+ stat = 0;
+ iunlock(&cp->reglock);
+
+ if(stat & Serr)
+ error(Eio);
+
+ /*
+ * wait for command to complete. if we get a note,
+ * remember it but keep waiting to let the disk finish
+ * the current command.
+ */
+ loop = 0;
+ while(waserror()){
+ DPRINT("interrupted ataxfer\n");
+ if(loop++ > 10){
+ print("ata disk error\n");
+ nexterror();
+ }
+ }
+ atasleep(cp, 3000);
+ dp->state = Sspinning;
+ dp->usetime = m->ticks;
+ poperror();
+ if(loop)
+ nexterror();
+
+ if(cp->status & Serr){
+ DPRINT("hd%ld err: lblk %ld status %ux, err %ux\n",
+ dp-ata, lblk, cp->status, cp->error);
+ DPRINT("\tcyl %d, sec %d, head %d\n", cyl, sec, head);
+ DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
+ atarepl(dp, lblk+cp->sofar);
+ error(Eio);
+ }
+ cp->buf = 0;
+ len = cp->sofar*dp->bytes;
+ qunlock(cp);
+ poperror();
+
+ return len;
+}
+
+/*
+ * set read ahead mode
+ */
+static void
+atasetbuf(Drive *dp, int on)
+{
+ Controller *cp = dp->cp;
+
+ qlock(cp);
+ if(waserror()){
+ qunlock(cp);
+ nexterror();
+ }
+
+ cmdreadywait(dp);
+
+ ilock(&cp->reglock);
+ cp->cmd = Csetbuf;
+ outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55); /* read look ahead */
+ outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
+ outb(cp->pbase+Pcmd, Csetbuf);
+ iunlock(&cp->reglock);
+
+ atasleep(cp, 5000);
+
+/* if(cp->status & Serr)
+ DPRINT("hd%d setbuf err: status %lux, err %lux\n",
+ dp-ata, cp->status, cp->error);/**/
+
+ poperror();
+ qunlock(cp);
+}
+
+/*
+ * ident sector from drive. this is from ANSI X3.221-1994
+ */
+struct Ident
+{
+ ushort config; /* general configuration info */
+ ushort cyls; /* # of cylinders (default) */
+ ushort reserved0;
+ ushort heads; /* # of heads (default) */
+ ushort b2t; /* unformatted bytes/track */
+ ushort b2s; /* unformated bytes/sector */
+ ushort s2t; /* sectors/track (default) */
+ ushort reserved1[3];
+/* 10 */
+ ushort serial[10]; /* serial number */
+ ushort type; /* buffer type */
+ ushort bsize; /* buffer size/512 */
+ ushort ecc; /* ecc bytes returned by read long */
+ ushort firm[4]; /* firmware revision */
+ ushort model[20]; /* model number */
+/* 47 */
+ ushort s2i; /* number of sectors/interrupt */
+ ushort dwtf; /* double word transfer flag */
+ ushort capabilities;
+ ushort reserved2;
+ ushort piomode;
+ ushort dmamode;
+ ushort cvalid; /* (cvald&1) if next 4 words are valid */
+ ushort ccyls; /* current # cylinders */
+ ushort cheads; /* current # heads */
+ ushort cs2t; /* current sectors/track */
+ ushort ccap[2]; /* current capacity in sectors */
+ ushort cs2i; /* current number of sectors/interrupt */
+/* 60 */
+ ushort lbasecs[2]; /* # LBA user addressable sectors */
+ ushort dmasingle;
+ ushort dmadouble;
+/* 64 */
+ ushort reserved3[64];
+ ushort vendor[32]; /* vendor specific */
+ ushort reserved4[96];
+};
+
+/*
+ * get parameters from the drive
+ */
+static void
+ataident(Drive *dp)
+{
+ Controller *cp;
+ char *buf;
+ Ident *ip;
+ char id[21];
+
+ cp = dp->cp;
+ buf = smalloc(Maxxfer);
+ qlock(cp);
+ if(waserror()){
+ cp->buf = 0;
+ qunlock(cp);
+ free(buf);
+ nexterror();
+ }
+
+ cmdreadywait(dp);
+
+ ilock(&cp->reglock);
+ cp->nsecs = 1;
+ cp->sofar = 0;
+ cp->cmd = Cident;
+ cp->dp = dp;
+ cp->buf = buf;
+ outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
+ outb(cp->pbase+Pcmd, Cident);
+ iunlock(&cp->reglock);
+
+ atasleep(cp, 5000);
+ if(cp->status & Serr){
+ DPRINT("bad disk ident status\n");
+ error(Eio);
+ }
+ ip = (Ident*)buf;
+
+ /*
+ * this function appears to respond with an extra interrupt after
+ * the ident information is read, except on the safari. The following
+ * delay gives this extra interrupt a chance to happen while we are quiet.
+ * Otherwise, the interrupt may come during a subsequent read or write,
+ * causing a panic and much confusion.
+ */
+ if (cp->cmd == Cident2)
+ tsleep(&cp->r, return0, 0, 10);
+
+ memmove(id, ip->model, sizeof(id)-1);
+ id[sizeof(id)-1] = 0;
+
+ if(ip->capabilities & (1<<9)){
+ dp->lba = 1;
+ dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16);
+ dp->cap = dp->bytes * dp->sectors;
+/*print("\nata%d model %s with %d lba sectors\n", dp->drive, id, dp->sectors);/**/
+ } else {
+ dp->lba = 0;
+
+ /* use default (unformatted) settings */
+ dp->cyl = ip->cyls;
+ dp->heads = ip->heads;
+ dp->sectors = ip->s2t;
+/*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->drive,
+ id, dp->cyl, dp->heads, dp->sectors);/**/
+
+ if(ip->cvalid&(1<<0)){
+ /* use current settings */
+ dp->cyl = ip->ccyls;
+ dp->heads = ip->cheads;
+ dp->sectors = ip->cs2t;
+/*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/
+ }
+ dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+ }
+ cp->lastcmd = cp->cmd;
+ cp->cmd = 0;
+ cp->buf = 0;
+ free(buf);
+ poperror();
+ qunlock(cp);
+}
+
+/*
+ * probe the given sector to see if it exists
+ */
+static int
+ataprobe(Drive *dp, int cyl, int sec, int head)
+{
+ Controller *cp;
+ char *buf;
+ int rv;
+
+ cp = dp->cp;
+ buf = smalloc(Maxxfer);
+ qlock(cp);
+ if(waserror()){
+ free(buf);
+ qunlock(cp);
+ nexterror();
+ }
+
+ cmdreadywait(dp);
+
+ ilock(&cp->reglock);
+ cp->cmd = Cread;
+ cp->dp = dp;
+ cp->status = 0;
+ cp->nsecs = 1;
+ cp->sofar = 0;
+
+ outb(cp->pbase+Pcount, 1);
+ outb(cp->pbase+Psector, sec+1);
+ outb(cp->pbase+Pdh, DHmagic | head | (dp->lba<<6) | (dp->drive<<4));
+ outb(cp->pbase+Pcyllsb, cyl);
+ outb(cp->pbase+Pcylmsb, cyl>>8);
+ outb(cp->pbase+Pcmd, Cread);
+ iunlock(&cp->reglock);
+
+ atasleep(cp, 5000);
+
+ if(cp->status & Serr)
+ rv = -1;
+ else
+ rv = 0;
+
+ cp->buf = 0;
+ free(buf);
+ poperror();
+ qunlock(cp);
+ return rv;
+}
+
+/*
+ * figure out the drive parameters
+ */
+static void
+ataparams(Drive *dp)
+{
+ int i, hi, lo;
+
+ /*
+ * first try the easy way, ask the drive and make sure it
+ * isn't lying.
+ */
+ dp->bytes = 512;
+ ataident(dp);
+ if(dp->lba){
+ i = dp->sectors - 1;
+ if(ataprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0)
+ return;
+ } else {
+ if(ataprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0)
+ return;
+ }
+
+ /*
+ * the drive lied, determine parameters by seeing which ones
+ * work to read sectors.
+ */
+ dp->lba = 0;
+ for(i = 0; i < 32; i++)
+ if(ataprobe(dp, 0, 0, i) < 0)
+ break;
+ dp->heads = i;
+ for(i = 0; i < 128; i++)
+ if(ataprobe(dp, 0, i, 0) < 0)
+ break;
+ dp->sectors = i;
+ for(i = 512; ; i += 512)
+ if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+ break;
+ lo = i - 512;
+ hi = i;
+ for(; hi-lo > 1;){
+ i = lo + (hi - lo)/2;
+ if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+ hi = i;
+ else
+ lo = i;
+ }
+ dp->cyl = lo + 1;
+ dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+}
+
+/*
+ * Read block replacement table.
+ * The table is just ascii block numbers.
+ */
+static void
+atareplinit(Drive *dp)
+{
+ char *line[Nrepl+1];
+ char *field[1];
+ ulong n;
+ int i;
+ char *buf;
+
+ /*
+ * check the partition is big enough
+ */
+ if(dp->repl.p->end - dp->repl.p->start < Nrepl+1){
+ dp->repl.p = 0;
+ return;
+ }
+
+ buf = smalloc(Maxxfer);
+ if(waserror()){
+ free(buf);
+ nexterror();
+ }
+
+ /*
+ * read replacement table from disk, null terminate
+ */
+ ataxfer(dp, dp->repl.p, Cread, 0, dp->bytes, buf);
+ buf[dp->bytes-1] = 0;
+
+ /*
+ * parse replacement table.
+ */
+ n = getfields(buf, line, Nrepl+1, 1, "\n");
+ if(strncmp(line[0], REPLMAGIC, sizeof(REPLMAGIC)-1)){
+ dp->repl.p = 0;
+ } else {
+ for(dp->repl.nrepl = 0, i = 1; i < n; i++, dp->repl.nrepl++){
+ if(getfields(line[i], field, 1, 1, " ") != 1)
+ break;
+ dp->repl.blk[dp->repl.nrepl] = strtoul(field[0], 0, 0);
+ if(dp->repl.blk[dp->repl.nrepl] <= 0)
+ break;
+ }
+ }
+ free(buf);
+ poperror();
+}
+
+/*
+ * read partition table. The partition table is just ascii strings.
+ */
+static void
+atapart(Drive *dp)
+{
+ Partition *pp;
+ char *line[Npart+1];
+ char *field[3];
+ ulong n;
+ int i;
+ char *buf;
+
+ sprint(dp->vol, "hd%ld", dp - ata);
+
+ /*
+ * we always have a partition for the whole disk
+ * and one for the partition table
+ */
+ pp = &dp->p[0];
+ strcpy(pp->name, "disk");
+ pp->start = 0;
+ pp->end = dp->cap / dp->bytes;
+ pp++;
+ strcpy(pp->name, "partition");
+ pp->start = dp->p[0].end - 1;
+ pp->end = dp->p[0].end;
+ pp++;
+ dp->npart = 2;
+
+ /*
+ * initialise the bad-block replacement info
+ */
+ dp->repl.p = 0;
+
+ buf = smalloc(Maxxfer);
+ if(waserror()){
+ free(buf);
+ nexterror();
+ }
+
+ /*
+ * read last sector from disk, null terminate. This used
+ * to be the sector we used for the partition tables.
+ * However, this sector is special on some PC's so we've
+ * started to use the second last sector as the partition
+ * table instead. To avoid reconfiguring all our old systems
+ * we first look to see if there is a valid partition
+ * table in the last sector. If so, we use it. Otherwise
+ * we switch to the second last.
+ */
+ ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf);
+ buf[dp->bytes-1] = 0;
+ n = getfields(buf, line, Npart+1, 1, "\n");
+ if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){
+ dp->p[0].end--;
+ dp->p[1].start--;
+ dp->p[1].end--;
+ ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf);
+ buf[dp->bytes-1] = 0;
+ n = getfields(buf, line, Npart+1, 1, "\n");
+ }
+
+ /*
+ * parse partition table.
+ */
+ if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){
+ for(i = 1; i < n; i++){
+ switch(getfields(line[i], field, 3, 1, " ")) {
+ case 2:
+ if(strcmp(field[0], "unit") == 0)
+ strncpy(dp->vol, field[1], KNAMELEN);
+ break;
+ case 3:
+ strncpy(pp->name, field[0], KNAMELEN);
+ if(strncmp(pp->name, "repl", KNAMELEN) == 0)
+ dp->repl.p = pp;
+ pp->start = strtoul(field[1], 0, 0);
+ pp->end = strtoul(field[2], 0, 0);
+ if(pp->start > pp->end || pp->end > dp->p[0].end)
+ break;
+ dp->npart++;
+ pp++;
+ }
+ }
+ }
+ free(buf);
+ poperror();
+
+ if(dp->repl.p)
+ atareplinit(dp);
+}
+
+enum
+{
+ Maxloop= 10000,
+};
+
+/*
+ * we get an interrupt for every sector transferred
+ */
+static void
+ataintr(Ureg*, void *arg)
+{
+ Controller *cp;
+ Drive *dp;
+ long loop;
+ char *addr;
+
+ cp = arg;
+ dp = cp->dp;
+
+ ilock(&cp->reglock);
+
+ loop = 0;
+ while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){
+ if(++loop > Maxloop) {
+ DPRINT("cmd=%ux status=%ux\n",
+ cp->cmd, inb(cp->pbase+Pstatus));
+ panic("ataintr: wait busy");
+ }
+ }
+
+ switch(cp->cmd){
+ case Cwrite:
+ if(cp->status & Serr){
+ cp->lastcmd = cp->cmd;
+ cp->cmd = 0;
+ cp->error = inb(cp->pbase+Perror);
+ wakeup(&cp->r);
+ break;
+ }
+ cp->sofar++;
+ if(cp->sofar < cp->nsecs){
+ loop = 0;
+ while(((cp->status = inb(cp->pbase+Pstatus)) & Sdrq) == 0)
+ if(++loop > Maxloop) {
+ DPRINT("cmd=%ux status=%ux\n",
+ cp->cmd, inb(cp->pbase+Pstatus));
+ panic("ataintr: write");
+ }
+ addr = cp->buf;
+ if(addr){
+ addr += cp->sofar*dp->bytes;
+ outss(cp->pbase+Pdata, addr, dp->bytes/2);
+ }
+ } else{
+ cp->lastcmd = cp->cmd;
+ cp->cmd = 0;
+ wakeup(&cp->r);
+ }
+ break;
+ case Cread:
+ case Cident:
+ loop = 0;
+ while((cp->status & (Serr|Sdrq)) == 0){
+ if(++loop > Maxloop) {
+ DPRINT("cmd=%ux status=%ux\n",
+ cp->cmd, inb(cp->pbase+Pstatus));
+ panic("ataintr: read/ident");
+ }
+ cp->status = inb(cp->pbase+Pstatus);
+ }
+ if(cp->status & Serr){
+ cp->lastcmd = cp->cmd;
+ cp->cmd = 0;
+ cp->error = inb(cp->pbase+Perror);
+ wakeup(&cp->r);
+ break;
+ }
+ addr = cp->buf;
+ if(addr){
+ addr += cp->sofar*dp->bytes;
+ inss(cp->pbase+Pdata, addr, dp->bytes/2);
+ }
+ cp->sofar++;
+ if(cp->sofar > cp->nsecs)
+ print("ataintr %d %d\n", cp->sofar, cp->nsecs);
+ if(cp->sofar >= cp->nsecs){
+ cp->lastcmd = cp->cmd;
+ if (cp->cmd == Cread)
+ cp->cmd = 0;
+ else
+ cp->cmd = Cident2;
+ wakeup(&cp->r);
+ }
+ break;
+ case Cinitparam:
+ case Csetbuf:
+ case Cidle:
+ case Cstandby:
+ case Cpowerdown:
+ cp->lastcmd = cp->cmd;
+ cp->cmd = 0;
+ wakeup(&cp->r);
+ break;
+ case Cident2:
+ cp->lastcmd = cp->cmd;
+ cp->cmd = 0;
+ break;
+ default:
+ print("weird disk interrupt, cmd=%.2ux, lastcmd= %.2ux status=%.2ux\n",
+ cp->cmd, cp->lastcmd, cp->status);
+ break;
+ }
+
+ iunlock(&cp->reglock);
+}
+
+void
+hardclock(void)
+{
+ int drive;
+ Drive *dp;
+ Controller *cp;
+ int diff;
+
+ if(spindowntime <= 0)
+ return;
+
+ for(drive = 0; drive < nhard; drive++){
+ dp = &ata[drive];
+ cp = dp->cp;
+
+ diff = TK2SEC(m->ticks - dp->usetime);
+ if((dp->state == Sspinning) && (diff >= spindowntime)){
+ ilock(&cp->reglock);
+ cp->cmd = Cstandby;
+ outb(cp->pbase+Pcount, 0);
+ outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | 0);
+ outb(cp->pbase+Pcmd, cp->cmd);
+ iunlock(&cp->reglock);
+ dp->state = Sstandby;
+ }
+ }
+}
+
+Dev atadevtab = {
+ 'H',
+ "ata",
+
+ devreset,
+ atainit,
+ devshutdown,
+ ataattach,
+ atawalk,
+ atastat,
+ ataopen,
+ devcreate,
+ ataclose,
+ ataread,
+ devbread,
+ atawrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
diff --git a/os/cerf1110/devcerf.c b/os/cerf1110/devcerf.c
new file mode 100644
index 00000000..3c0fc999
--- /dev/null
+++ b/os/cerf1110/devcerf.c
@@ -0,0 +1,128 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "io.h"
+
+
+enum{
+ Qdir,
+ Qled,
+};
+
+static
+Dirtab cerftab[]={
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "cerfled", {Qled, 0}, 0, 0660,
+};
+
+static void
+cerfinit(void) /* default in dev.c */
+{
+ int s;
+
+ s = splhi();
+ GPIOREG->gpdr |= 0xF;
+ GPIOREG->gpsr = 1<<0; /* we're here */
+ splx(s);
+}
+
+static Chan*
+cerfattach(char* spec)
+{
+ return devattach('T', spec);
+}
+
+static Walkqid*
+cerfwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, cerftab, nelem(cerftab), devgen);
+}
+
+static int
+cerfstat(Chan* c, uchar *db, int n)
+{
+ return devstat(c, db, n, cerftab, nelem(cerftab), devgen);
+}
+
+static Chan*
+cerfopen(Chan* c, int omode)
+{
+ return devopen(c, omode, cerftab, nelem(cerftab), devgen);
+}
+
+static void
+cerfclose(Chan* c)
+{
+ USED(c);
+}
+
+static long
+cerfread(Chan* c, void* a, long n, vlong offset)
+{
+ char buf[16];
+
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, a, n, cerftab, nelem(cerftab), devgen);
+ case Qled:
+ snprint(buf, sizeof(buf), "%2.2lux", GPIOREG->gplr&0xF);
+ return readstr(offset, a, n, buf);
+ default:
+ n=0;
+ break;
+ }
+ return n;
+}
+
+static long
+cerfwrite(Chan* c, void* a, long n, vlong)
+{
+ char buf[16];
+ ulong v;
+
+ switch((ulong)c->qid.path){
+ case Qled:
+ if(n >= sizeof(buf))
+ n = sizeof(buf)-1;
+ memmove(buf, a, n);
+ buf[n] = 0;
+ v = GPIOREG->gplr & 0xF;
+ if(buf[0] == '+')
+ v |= strtoul(buf+1, nil, 0);
+ else if(buf[0] == '-')
+ v &= ~strtoul(buf+1, nil, 0);
+ else
+ v = strtoul(buf, nil, 0);
+ GPIOREG->gpsr = v & 0xF;
+ GPIOREG->gpcr = ~v & 0xF;
+ break;
+ default:
+ error(Ebadusefd);
+ }
+ return n;
+}
+
+Dev cerfdevtab = {
+ 'T',
+ "cerf",
+
+ devreset,
+ cerfinit,
+ devshutdown,
+ cerfattach,
+ cerfwalk,
+ cerfstat,
+ cerfopen,
+ devcreate,
+ cerfclose,
+ cerfread,
+ devbread,
+ cerfwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
diff --git a/os/cerf1110/ether8900.c b/os/cerf1110/ether8900.c
new file mode 100644
index 00000000..fc9ea12e
--- /dev/null
+++ b/os/cerf1110/ether8900.c
@@ -0,0 +1,702 @@
+/*
+ * Crystal CS8900 ethernet controller
+ *
+ * Todo:
+ * - promiscuous
+ *
+ * Copyright © 1998 Vita Nuova Limited. All rights reserved.
+ * Revisions Copyright © 2000,2003 Vita Nuova Holdings Limited. All rights reserved.
+ */
+
+#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"
+
+typedef struct Ctlr Ctlr;
+
+/*
+ * The CS8900 can be addressed from either ISA I/O space
+ * or ISA memory space at the following virtual addresses,
+ * depending on the hardware's wiring. MEMORY controls
+ * use of memory space.
+ * The cs8900 address pins are shifted by 1 relative to the CPU.
+ */
+enum {//18000000
+ IsaIOBase = 0x08000000,
+ IsaMemBase = 0xe0000000,
+
+ IOBase = 0x300,
+ MemBase = 0xc0000,
+
+ MEMORY = 0, /* set non-zero if memory mode to be used */
+ DORESET = 1, /* send soft-reset during initialisation */
+ DEBUG = 0,
+};
+
+#define IOSHIFT 0 /* was 2 */
+#define IOREG(r) (IsaIOBase+((IOBase+(r))<<IOSHIFT))
+
+/* I/O accesses */
+#define out16(port, val) (*((ushort *)IOREG(port)) = (val))
+#define in16(port) *((ushort *)IOREG(port))
+#define in8(port) *((uchar *)IOREG(port))
+#define regIOw(reg, val) do {out16(PpPtr, (reg)|0x3000); out16(PpData, val);} while(0)
+#define regIOr(reg) (out16(PpPtr, (reg)|0x3000), in16(PpData))
+#define regIOr1(reg) (out16(PpPtr, (reg)|0x3000), in16(PpData1))
+
+/* Memory accesses */
+
+#define REGW(reg, val) *((ushort *)IsaMemBase + MemBase + (reg)) = (val)
+#define REGR(reg) *((ushort *)IsaMemBase + MemBase + (reg))
+
+enum { /* I/O Mode Register Offsets */
+ RxTxData = 0x00, /* receive/transmit data - port 0 */
+ RxTxData1 = 0x02, /* r/t data port 1 */
+ TxCmdIO = 0x04, /* transmit command */
+ TxLenIO = 0x06, /* transmit length */
+ IsqIO = 0x08, /* Interrupt status queue */
+ PpPtr = 0x0a, /* packet page pointer */
+ PpData = 0x0c, /* packet page data */
+ PpData1 = 0x0e, /* packet page data - port 1*/
+};
+
+enum { /* Memory Mode Register Offsets */
+ /* Bus Interface Registers */
+ Ern = 0x0000, /* EISA registration numberion */
+ Pic = 0x0002, /* Product identification code */
+ Iob = 0x0020, /* I/O base address */
+ Intr = 0x0022, /* interrupt number */
+ Mba = 0x002c, /* memory base address */
+
+ Ecr = 0x0040, /* EEPROM command register */
+ Edw = 0x0042, /* EEPROM data word */
+ Rbc = 0x0050, /* receive frame byte counter */
+
+ /* Status and Control Registers */
+ RxCfg = 0x0102,
+ RxCtl = 0x0104,
+ TxCfg = 0x0106,
+ BufCfg = 0x010a,
+ LineCtl = 0x0112,
+ SelfCtl = 0x0114,
+ BusCtl = 0x0116,
+ TestCtl = 0x0118,
+ Isq = 0x0120,
+ RxEvent = 0x0124,
+ TxEvent = 0x0128,
+ BufEvent = 0x012c,
+ RxMISS = 0x0130,
+ TxCol = 0x0132,
+ LineSt = 0x0134,
+ SelfSt = 0x0136,
+ BusSt = 0x0138,
+ Tdr = 0x013c,
+
+ /* Initiate Transmit Registers */
+ TxCmd = 0x0144, /* transmit command */
+ TxLen = 0x0146, /* transmit length */
+
+ /* Address Filter Registers */
+ IndAddr = 0x0158, /* individual address registers */
+
+ /* Frame Location */
+ RxStatus = 0x0400, /* receive status */
+ RxLen = 0x0402, /* receive length */
+ RxFrame = 0x0404, /* receive frame location */
+ TxFrame = 0x0a00, /* transmit frame location */
+};
+
+enum { /* Ecr */
+ Addr = 0x00ff, /* EEPROM word address (field) */
+ Opcode = 0x0300, /* command opcode (field) */
+ EEread = 0x0200,
+ EEwrite = 0x0100,
+};
+
+enum { /* Isq */
+ Regnum = 0x003f, /* register number held by Isq (field) */
+ IsqRxEvent = 0x04,
+ IsqTxEvent = 0x08,
+ IsqBufEvent = 0x0c,
+ IsqRxMiss = 0x10,
+ IsqTxCol = 0x12,
+ RegContent = 0xffc0, /* register data contents (field) */
+};
+
+enum { /* RxCfg */
+ Skip_1 = 0x0040,
+ StreamE = 0x0080,
+ RxOKiE = 0x0100,
+ RxDMAonly = 0x0200,
+ AutoRxDMAE = 0x0400,
+ BufferCRC = 0x0800,
+ CRCerroriE = 0x1000,
+ RuntiE = 0x2000,
+ ExtradataiE = 0x4000,
+};
+
+enum { /* RxEvent */
+ IAHash = 0x0040,
+ Dribblebits = 0x0080,
+ RxOK = 0x0100,
+ Hashed = 0x0200,
+ IndividualAdr = 0x0400,
+ Broadcast = 0x0800,
+ CRCerror = 0x1000,
+ Runt = 0x2000,
+ Extradata = 0x4000,
+};
+
+enum { /* RxCtl */
+ IAHashA = 0x0040,
+ PromiscuousA = 0x0080,
+ RxOKA = 0x0100,
+ MulticastA = 0x0200,
+ IndividualA = 0x0400,
+ BroadcastA = 0x0800,
+ CRCerrorA = 0x1000,
+ RuntA = 0x2000,
+ ExtradataA = 0x4000,
+};
+
+enum { /* TxCfg */
+ LossofCRSiE = 0x0040,
+ SQEerroriE = 0x0080,
+ TxOKiE = 0x0100,
+ OutofWindowiE = 0x0200,
+ JabberiE = 0x0400,
+ AnycolliE = 0x0800,
+ Coll16iE = 0x8000,
+};
+
+enum { /* TxEvent */
+ LossofCRS = 0x0040,
+ SQEerror = 0x0080,
+ TxOK = 0x0100,
+ OutofWindow = 0x0200,
+ Jabber = 0x0400,
+ NTxCols = 0x7800, /* number of Tx collisions (field) */
+ coll16 = 0x8000,
+};
+
+enum { /* BufCfg */
+ SWintX = 0x0040,
+ RxDMAiE = 0x0080,
+ Rdy4TxiE = 0x0100,
+ TxUnderruniE = 0x0200,
+ RxMissiE = 0x0400,
+ Rx128iE = 0x0800,
+ TxColOvfiE = 0x1000,
+ MissOvfloiE = 0x2000,
+ RxDestiE = 0x8000,
+};
+
+enum { /* BufEvent */
+ SWint = 0x0040,
+ RxDMAFrame = 0x0080,
+ Rdy4Tx = 0x0100,
+ TxUnderrun = 0x0200,
+ RxMiss = 0x0400,
+ Rx128 = 0x0800,
+ RxDest = 0x8000,
+};
+
+enum { /* RxMiss */
+ MissCount = 0xffc0,
+};
+
+enum { /* TxCol */
+ ColCount = 0xffc0,
+};
+
+enum { /* LineCtl */
+ SerRxOn = 0x0040,
+ SerTxOn = 0x0080,
+ Iface = 0x0300, /* (field) 01 - AUI, 00 - 10BASE-T, 10 - Auto select */
+ ModBackoffE = 0x0800,
+ PolarityDis = 0x1000,
+ DefDis = 0x2000,
+ LoRxSquelch = 0x4000,
+};
+
+enum { /* LineSt */
+ LinkOK = 0x0080,
+ AUI = 0x0100,
+ TenBT = 0x0200,
+ PolarityOK = 0x1000,
+ CRS = 0x4000,
+};
+
+enum { /* SelfCtl */
+ RESET = 0x0040,
+ SWSuspend = 0x0100,
+ HWSleepE = 0x0200,
+ HWStandbyE = 0x0400,
+};
+
+enum { /* SelfSt */
+ Active3V = 0x0040,
+ INITD = 0x0080,
+ SIBUSY = 0x0100,
+ EepromPresent = 0x0200,
+ EepromOK = 0x0400,
+ ElPresent = 0x0800,
+ EeSize = 0x1000,
+};
+
+enum { /* BusCtl */
+ ResetRxDMA = 0x0040,
+ UseSA = 0x0200,
+ MemoryE = 0x0400,
+ DMABurst = 0x0800,
+ EnableIRQ = 0x8000,
+};
+
+enum { /* BusST */
+ TxBidErr = 0x0080,
+ Rdy4TxNOW = 0x0100,
+};
+
+enum { /* TestCtl */
+ FDX = 0x4000, /* full duplex */
+};
+
+enum { /* TxCmd */
+ TxStart = 0x00c0, /* bytes before transmit starts (field) */
+ TxSt5 = 0x0000, /* start after 5 bytes */
+ TxSt381 = 0x0040, /* start after 381 bytes */
+ TxSt1021 = 0x0080, /* start after 1021 bytes */
+ TxStAll = 0x00c0, /* start after the entire frame is in the cs8900 */
+ Force = 0x0100,
+ Onecoll = 0x0200,
+ InhibitCRC = 0x1000,
+ TxPadDis = 0x2000,
+};
+
+enum { /* EEPROM format */
+ Edataoff = 0x1C, /* start of data (ether address) */
+ Edatalen = 0x14, /* data count in 16-bit words */
+};
+
+struct Ctlr {
+ Lock;
+ Block* waiting; /* waiting for space in FIFO */
+ int model;
+ int rev;
+
+ ulong collisions;
+};
+
+static void
+regw(int reg, int val)
+{
+ if(DEBUG)
+ print("r%4.4ux <- %4.4ux\n", reg, val);
+ if(MEMORY){
+ REGW(reg, val);
+ }else{
+ out16(PpPtr, reg);
+ out16(PpData, val);
+ }
+}
+
+static int
+regr(int reg)
+{
+ int v;
+
+ if(MEMORY)
+ return REGR(reg);
+ out16(PpPtr, reg);
+ v = in16(PpData);
+ if(DEBUG)
+ print("r%4.4ux = %4.4ux\n", reg, v);
+ return v;
+}
+
+/*
+ * copy frames in and out, accounting for shorts aligned as longs in IO memory
+ */
+
+static void
+copypktin(void *ad, int len)
+{
+ ushort *s, *d;
+ int ns;
+
+ if(!MEMORY){
+ d = ad;
+ /*
+ * contrary to data sheet DS271PP3 pages 77-78,
+ * the data is not preceded by status & length
+ * perhaps because it has been read directly.
+ */
+ for(ns = len>>1; --ns >= 0;)
+ *d++ = in16(RxTxData);
+ if(len & 1)
+ *(uchar*)d = in16(RxTxData);
+ return;
+ }
+ d = ad;
+ s = (ushort*)IsaMemBase + MemBase + RxFrame;
+ for(ns = len>>1; --ns >= 0;){
+ *d++ = *s;
+ s += 2;
+ }
+ if(len & 1)
+ *(uchar*)d = *s;
+}
+
+static void
+copypktout(void *as, int len)
+{
+ ushort *s, *d;
+ int ns;
+
+ if(!MEMORY){
+ s = as;
+ ns = (len+1)>>1;
+ while(--ns >= 0)
+ out16(RxTxData, *s++);
+ return;
+ }
+ s = as;
+ d = (ushort*)IsaMemBase + MemBase + TxFrame;
+ ns = (len+1)>>1;
+ while(--ns >= 0){
+ *d = *s++;
+ d += 2;
+ }
+}
+
+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 = malloc(READSTR);
+ len = snprint(p, READSTR, "Overflow: %ud\n", ether->overflows);
+ len += snprint(p+len, READSTR-len, "CRC Error: %ud\n", ether->crcs);
+ snprint(p+len, READSTR-len, "Collision Seen: %lud\n", ctlr->collisions);
+
+ n = readstr(offset, a, n, p);
+ free(p);
+
+ return n;
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+ USED(arg, on);
+}
+
+static void
+attach(Ether *ether)
+{
+ int reg;
+
+ USED(ether);
+ /* enable transmit and receive */
+ reg = regr(BusCtl);
+ regw(BusCtl, reg|EnableIRQ);
+ reg = regr(LineCtl);
+ regw(LineCtl, reg|SerRxOn|SerTxOn);
+ if(DEBUG){
+ iprint("bus=%4.4ux line=%4.4ux\n", regr(BusCtl), regr(LineCtl));
+ iprint("rc=%4.4ux tc=%4.4ux bc=%4.4ux\n", regr(RxCfg), regr(TxCfg), regr(BufCfg));
+ }
+}
+
+static void
+txstart(Ether *ether, int dowait)
+{
+ int len, status;
+ Ctlr *ctlr;
+ Block *b;
+
+ ctlr = ether->ctlr;
+ for(;;){
+ if((b = ctlr->waiting) == nil){
+ if((b = qget(ether->oq)) == nil)
+ break;
+ }else{
+ if(!dowait)
+ break;
+ ctlr->waiting = nil;
+ }
+ len = BLEN(b);
+ if(MEMORY){
+ regw(TxCmd, TxSt381);
+ regw(TxLen, len);
+ }else{
+ out16(TxCmdIO, TxStAll);
+ out16(TxLenIO, len);
+ }
+ status = regr(BusSt);
+ if((status & Rdy4TxNOW) == 0) {
+ ctlr->waiting = b;
+ break;
+ }
+ /*
+ * Copy the packet to the transmit buffer.
+ */
+ copypktout(b->rp, len);
+ freeb(b);
+ }
+}
+
+static void
+transmit(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(ctlr);
+ txstart(ether, 0);
+ iunlock(ctlr);
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+ int len, events, status;
+ Block *b;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+ ilock(ctlr);
+ while((events = (MEMORY?regr(Isq):in16(IsqIO))) != 0) {
+ status = events&RegContent;
+ if(DEBUG)
+ iprint("status %4.4ux event %4.4ux\n", status, events);
+ switch(events&Regnum) {
+
+ case IsqBufEvent:
+ if(status&Rdy4Tx) {
+ if((b = ctlr->waiting) != nil){
+ ctlr->waiting = nil;
+ copypktout(b->rp, BLEN(b));
+ freeb(b);
+ /* wait for IsqTxEvent to send remaining packets in txstart */
+ }else
+ txstart(ether, 0);
+ }
+ break;
+
+ case IsqRxEvent:
+ if(status&RxOK) {
+ len = regr(RxLen);
+ if(DEBUG)
+ iprint("rxlen=%d\n", len);
+ if((b = iallocb(len)) != 0) {
+ copypktin(b->wp, len);
+ b->wp += len;
+ etheriq(ether, b, 1);
+ }
+ }
+ break;
+
+ case IsqTxEvent:
+ if(status&TxOK)
+ txstart(ether, 1);
+ break;
+
+ case IsqRxMiss:
+ ether->overflows++;
+ break;
+
+ case IsqTxCol:
+ ctlr->collisions++;
+ break;
+ }
+ }
+ iunlock(ctlr);
+}
+
+static int
+eepromwait(void)
+{
+ int i;
+
+ for(i=0; i<100000; i++)
+ if((regIOr(SelfSt) & SIBUSY) == 0)
+ return 0;
+ return -1;
+}
+
+static int
+eepromrd(void *buf, int off, int n)
+{
+ int i;
+ ushort *p;
+
+ p = buf;
+ n /= 2;
+ for(i=0; i<n; i++){
+ if(eepromwait() < 0)
+ return -1;
+ regIOw(Ecr, EEread | (off+i));
+ if(eepromwait() < 0)
+ return -1;
+ p[i] = regIOr(Edw);
+ }
+ return 0;
+}
+
+static int
+reset(Ether* ether)
+{
+ int i, reg, easet;
+ uchar ea[Eaddrlen];
+ ushort buf[Edatalen];
+ Ctlr *ctlr;
+
+ if(!MEMORY)
+ mmuphysmap(IsaIOBase, 64*1024);
+
+ delay(120); /* allow time for chip to reset */
+
+ if(0){
+ *(ushort*)IsaIOBase = 0xDEAD; /* force rubbish on bus */
+ for(i=0; i<100; i++){
+ if(in16(PpPtr) == 0x3000)
+ break;
+ delay(1);
+ }
+ if(i>=100){
+ iprint("failed init: reg(0xA): %4.4ux, should be 0x3000\n", in16(PpPtr));
+ return -1;
+ }
+ }
+iprint("8900: %4.4ux (selfst) %4.4ux (linest)\n", regIOr(SelfSt), regIOr(LineSt));
+iprint("8900: %4.4ux %4.4ux\n", regIOr(Ern), regIOr(Pic));
+
+ /*
+ * Identify the chip by reading the Pic register.
+ * The EISA registration number is in the low word
+ * and the product identification code in the high code.
+ * The ERN for Crystal Semiconductor is 0x630e.
+ * Bits 0-7 and 13-15 of the Pic should be zero for a CS8900.
+ */
+ if(regIOr(Ern) != 0x630e || (regIOr(Pic) & 0xe0ff) != 0)
+ return -1;
+
+ if(ether->ctlr == nil)
+ ether->ctlr = malloc(sizeof(Ctlr));
+ ctlr = ether->ctlr;
+
+ reg = regIOr(Pic);
+ ctlr->model = reg>>14;
+ ctlr->rev = (reg >> 8) & 0x1F;
+
+ ether->mbps = 10;
+
+ memset(ea, 0, Eaddrlen);
+ easet = memcmp(ea, ether->ea, Eaddrlen);
+ memset(buf, 0, sizeof(buf));
+ if(regIOr(SelfSt) & EepromPresent) { /* worth a look */
+ if(eepromrd(buf, Edataoff, sizeof(buf)) >= 0){
+ for(i=0; i<3; i++){
+ ether->ea[2*i] = buf[i];
+ ether->ea[2*i+1] = buf[i] >> 8;
+ }
+ easet = 1;
+ }else
+ iprint("cs8900: can't read EEPROM\n");
+ }
+ if(!easet){
+ iprint("cs8900: ethernet address not configured\n");
+ return -1;
+ }
+ memmove(ea, ether->ea, Eaddrlen);
+
+ if(DORESET){
+ /*
+ * Reset the chip and ensure 16-bit mode operation
+ */
+ regIOw(SelfCtl, RESET);
+ delay(10);
+ i=in8(PpPtr); USED(i);
+ i=in8(PpPtr+1); USED(i);
+ i=in8(PpPtr); USED(i);
+ i=in8(PpPtr+1); USED(i);
+
+ /*
+ * Wait for initialisation and EEPROM reads to complete
+ */
+ i=0;
+ for(;;) {
+ short st = regIOr(SelfSt);
+ if((st&SIBUSY) == 0 && st&INITD)
+ break;
+ if(i++ > 1000000)
+ panic("cs8900: initialisation failed");
+ }
+ }
+
+ if(MEMORY){
+ /*
+ * Enable memory mode operation.
+ */
+ regIOw(Mba, MemBase & 0xffff);
+ regIOw(Mba+2, MemBase >> 16);
+ regIOw(BusCtl, MemoryE|UseSA);
+ }
+
+ /*
+ * Enable 10BASE-T half duplex, transmit in interrupt mode
+ */
+ reg = regr(LineCtl);
+ regw(LineCtl, reg&~Iface);
+ reg = regr(TestCtl);
+ if(ether->fullduplex)
+ regw(TestCtl, reg|FDX);
+ else
+ regw(TestCtl, reg&~FDX);
+ regw(BufCfg, Rdy4TxiE|TxUnderruniE);
+ regw(TxCfg, TxOKiE|AnycolliE|LossofCRSiE|Coll16iE);
+ regw(RxCfg, RxOKiE|CRCerroriE|RuntiE|ExtradataiE);
+ regw(RxCtl, RxOKA|IndividualA|BroadcastA);
+
+ for(i=0; i<Eaddrlen; i+=2)
+ regw(IndAddr+i, ea[i] | (ea[i+1] << 8));
+
+ /* IRQ tied to INTRQ0 */
+ regw(Intr, 0);
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ ether->attach = attach;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt;
+ ether->ifstat = ifstat;
+
+ ether->arg = ether;
+ ether->promiscuous = promiscuous;
+
+ ether->itype = BusGPIOrising; /* TO DO: this shouldn't be done here */
+
+ return 0;
+}
+
+void
+ether8900link(void)
+{
+ addethercard("CS8900", reset);
+}
diff --git a/os/cerf1110/fns.h b/os/cerf1110/fns.h
new file mode 100644
index 00000000..2ad379e4
--- /dev/null
+++ b/os/cerf1110/fns.h
@@ -0,0 +1,179 @@
+#include "../port/portfns.h"
+
+ulong aifinit(uchar *aifarr);
+void aamloop(int);
+int archaudiopower(int);
+void archaudiomute(int);
+void archaudioamp(int);
+int archaudiospeed(int, int);
+void archcodecreset(void);
+void archconfinit(void);
+void archconsole(void);
+int archflash12v(int);
+int archhooksw(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, int direction, int bigend, void(*)(void*,ulong), void*);
+int dmastart(Dma*, 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 getcpsr(void);
+ulong getcpuid(void);
+ulong getspsr(void);
+void gotopc(ulong);
+
+void icflushall(void);
+void _idlemode(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, void (*)(Ureg*, void*), void*, int, char*);
+void intrenable(int, void (*)(Ureg*, void*), void*, int, 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 L3init(void);
+int L3read(int, void*, int);
+int L3write(int, void*, int);
+void lcd_setbacklight(int);
+void lcd_sethz(int);
+void lights(ulong);
+void links(void);
+ulong mcpgettfreq(void);
+void mcpinit(void);
+void mcpsettfreq(ulong tfreq);
+void mcpspeaker(int, int);
+void mcptelecomsetup(ulong hz, int adm, int xint, int rint);
+ushort mcpadcread(int ts);
+void mcptouchsetup(int ts);
+void mcptouchintrenable(void);
+void mcptouchintrdisable(void);
+void mcpgpiowrite(ushort mask, ushort data);
+void mcpgpiosetdir(ushort mask, ushort dir);
+ushort mcpgpioread(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 nowriteSeg(void *, 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)
+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));
+int uartprint(char*, ...);
+void uartspecial(int, int, char, 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))
+
+// #define timer_start() (*OSCR)
+// #define timer_ticks(t) (*OSCR - (ulong)(t))
+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);
diff --git a/os/cerf1110/io.h b/os/cerf1110/io.h
new file mode 100644
index 00000000..4e8cbc39
--- /dev/null
+++ b/os/cerf1110/io.h
@@ -0,0 +1 @@
+#include "../sa1110/sa1110io.h"
diff --git a/os/cerf1110/main.c b/os/cerf1110/main.c
new file mode 100644
index 00000000..58bd792f
--- /dev/null
+++ b/os/cerf1110/main.c
@@ -0,0 +1,255 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "version.h"
+
+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;
+
+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);
+}
+
+void
+main(void)
+{
+ memset(edata, 0, end-edata); /* clear the BSS */
+ memset(m, 0, sizeof(Mach)); /* clear the mach struct */
+ conf.nmach = 1;
+ archreset();
+ dmareset();
+ quotefmtinstall();
+ confinit();
+ xinit();
+ mmuinit();
+ poolinit();
+ poolsizeinit();
+ trapinit();
+ clockinit();
+ printinit();
+ screeninit();
+ procinit();
+ links();
+ chandevreset();
+
+ eve = strdup("inferno");
+
+ archconsole();
+ 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);
+
+ userinit();
+ schedinit();
+}
+
+void
+reboot(void)
+{
+ exit(0);
+}
+
+void
+halt(void)
+{
+ spllo();
+ print("cpu halted\n");
+ for(;;){
+ /* nothing to do */
+ }
+}
+
+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/cerf1110/mem.h b/os/cerf1110/mem.h
new file mode 100644
index 00000000..19d55b07
--- /dev/null
+++ b/os/cerf1110/mem.h
@@ -0,0 +1,200 @@
+/*
+ * 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 0xC0000000
+#define MACHADDR (KZERO+0x00001000)
+#define KTTB (KZERO+0x00004000)
+#define KTZERO (KZERO+0x00008010)
+#define KSTACK 8192 /* Size of kernel stack */
+#define FLASHMEM 0x50000000 /* map flash at phys 0 to otherwise unused virtual space */
+#define FLUSHMEM 0xE0000000 /* internally decoded zero memory (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 0xC8000000 /* 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 0x08000000 /* static chip select 1 */
+#define PHYSCS2 0x10000000 /* static chip select 2 */
+#define PHYSCS3 0x18000000 /* static chip select 3 */
+#define PHYSPCMCIA0 0x20000000 /* PCMCIA socket 0 space */
+#define PHYSPCMCIA1 0x30000000 /* PCMCIA socket 1 space */
+#define PCMCIASIZE 0x10000000 /* they're both huge */
+#define PHYSCS4 0x40000000 /* static chip select 4 */
+#define PHYSCS5 0x48000000 /* static chip select 5 */
+#define PHYSSERIAL(n) (0x80000000+0x10000*(n)) /* serial devices */
+#define PHYSUSB 0x80000000
+#define PHYSGPCLK 0x80020060
+#define PHYSMCP 0x80060000
+#define PHYSSSP 0x80070060
+#define PHYSOSTMR 0x90000000 /* timers */
+#define PHYSRTC 0x90010000 /* real time clock */
+#define PHYSPOWER 0x90020000 /* power management registers */
+#define PHYSRESET 0x90030000 /* reset controller */
+#define PHYSGPIO 0x90040000
+#define PHYSINTR 0x90050000 /* interrupt controller */
+#define PHYSPPC 0x90060000 /* peripheral pin controller */
+#define PHYSMEMCFG 0xA0000000 /* memory configuration */
+#define PHYSDMA 0xB0000000 /* DMA controller */
+#define PHYSLCD 0xB0100000 /* LCD controller */
+#define PHYSMEM0 0xC0000000
+#define PHYSFLUSH0 0xE0000000 /* internally decoded, for cache flushing */
+
+/*
+ * Memory Interface Control Registers
+ */
+#define MDCNFG (PHYSMEMCFG) /* memory controller configuration */
+#define MDCAS0 (PHYSMEMCFG+4)
+#define MDCAS1 (PHYSMEMCFG+8)
+#define MDCAS2 (PHYSMEMCFG+0xC)
+#define MSC0 (PHYSMEMCFG+0x10)
+#define MSC1 (PHYSMEMCFG+0x14)
+#define MSC2 (PHYSMEMCFG+0x2C) /* SA1110, but not SA1100 */
+
+#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)
+
+/*
+ * 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: */
+#define CpControl 1 /* R/W: */
+#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 CpRBops 9 /* W: Read Buffer operations */
+#define CpPID 13 /* R/W: Process ID Virtual Mapping */
+#define CpDebug 14 /* R/W: debug registers */
+#define CpTest 15 /* W: Test, Clock and Idle Control */
+
+/*
+ * Coprocessors
+ */
+#define CpMMU 15
+#define CpPWR 15
+
+/*
+ * CpControl bits
+ */
+#define CpCmmu 0x00000001 /* M: MMU enable */
+#define CpCalign 0x00000002 /* A: alignment fault enable */
+#define CpCDcache 0x00000004 /* C: instruction/data cache on */
+#define CpCwb 0x00000008 /* W: write buffer turned on */
+#define CpCi32 0x00000010 /* P: 32-bit programme space */
+#define CpCd32 0x00000020 /* D: 32-bit data space */
+#define CpCbe 0x00000080 /* B: big-endian operation */
+#define CpCsystem 0x00000100 /* S: system permission */
+#define CpCrom 0x00000200 /* R: ROM permission */
+#define CpCIcache 0x00001000 /* I: Instruction Cache on */
+#define CpCaltivec 0x00002000 /* X: alternative interrupt vectors */
+
+/*
+ * MMU
+ */
+/*
+ * Small pages:
+ * L1: 12-bit index -> 4096 descriptors -> 16Kb
+ * L2: 8-bit index -> 256 descriptors -> 1Kb
+ * Each L2 descriptor has access permissions for 4 1Kb sub-pages.
+ *
+ * TTB + L1Tx gives address of L1 descriptor
+ * L1 descriptor gives PTBA
+ * PTBA + L2Tx gives address of L2 descriptor
+ * L2 descriptor gives PBA
+ */
+#define MmuSection (1<<20)
+#define MmuLargePage (1<<16)
+#define MmuSmallPage (1<<12)
+#define MmuTTB(pa) ((pa) & ~0x3FFF) /* translation table base */
+#define MmuL1x(pa) (((pa)>>20) & 0xFFF) /* L1 table index */
+#define MmuPTBA(pa) ((pa) & ~0x3FF) /* page table base address */
+#define MmuL2x(pa) (((pa)>>12) & 0xFF) /* L2 table index */
+#define MmuPBA(pa) ((pa) & ~0xFFF) /* page base address */
+#define MmuSBA(pa) ((pa) & ~0xFFFFF) /* section base address */
+
+#define MmuL1type 0x03
+#define MmuL1page 0x01 /* descriptor is for L2 pages */
+#define MmuL1section 0x02 /* descriptor is for section */
+
+#define MmuL2invalid 0x000
+#define MmuL2large 0x001 /* large */
+#define MmuL2small 0x002 /* small */
+#define MmuWB 0x004 /* data goes through write buffer */
+#define MmuIDC 0x008 /* data placed in cache */
+
+#define MmuDAC(d) (((d) & 0xF)<<5) /* L1 domain */
+#define MmuAP(i, v) ((v)<<(((i)*2)+4)) /* access permissions */
+#define MmuL1AP(v) MmuAP(3, (v))
+#define MmuL2AP(v) MmuAP(3, (v))|MmuAP(2, (v))|MmuAP(1, (v))|MmuAP(0, (v))
+#define MmuAPsro 0 /* supervisor ro if S|R */
+#define MmuAPsrw 1 /* supervisor rw */
+#define MmuAPuro 2 /* supervisor rw + user ro */
+#define MmuAPurw 3 /* supervisor rw + user rw */
diff --git a/os/cerf1110/mkfile b/os/cerf1110/mkfile
new file mode 100644
index 00000000..693f3248
--- /dev/null
+++ b/os/cerf1110/mkfile
@@ -0,0 +1,101 @@
+<../../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=0xC0008010
+
+OBJ=\
+ l.$O\
+ clock.$O\
+ dma.$O\
+ fpi.$O\
+ fpiarm.$O\
+ fpimem.$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\
+ ../sa1110/sa1110io.h\
+ ../sa1110/fpi.h\
+
+CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I../sa1110
+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 -H5 -T0xC0008010 -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 -T0xC0008010 -R4 -l $OBJ $CONF.$O $LIBFILES
+
+<../port/portmkfile
+
+%.$O: ../sa1110/%.c
+ $CC $CFLAGS -I. ../sa1110/$stem.c
+
+%.$O: ../sa1110/%.s
+ $AS -I. -I../sa1110 ../sa1110/$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: ../sa1110/etherif.h ../port/netif.h
+$IP devip.$O: ../ip/ip.h
+io.h:N: ../sa1110/sa1110io.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: ../sa1110/etherif.h
+
+devuart.$O: ../sa1110/devuart.c
+ $CC $CFLAGS ../sa1110/devuart.c