diff options
Diffstat (limited to 'os/cerf250')
| -rw-r--r-- | os/cerf250/NOTICE | 2 | ||||
| -rw-r--r-- | os/cerf250/README | 44 | ||||
| -rw-r--r-- | os/cerf250/archcerf.c | 266 | ||||
| -rw-r--r-- | os/cerf250/cerf | 163 | ||||
| -rw-r--r-- | os/cerf250/dat.h | 135 | ||||
| -rw-r--r-- | os/cerf250/devpcf8563.c | 371 | ||||
| -rw-r--r-- | os/cerf250/ether91c111.c | 1056 | ||||
| -rw-r--r-- | os/cerf250/fns.h | 168 | ||||
| -rw-r--r-- | os/cerf250/io.h | 1 | ||||
| -rw-r--r-- | os/cerf250/main.c | 351 | ||||
| -rw-r--r-- | os/cerf250/mem.h | 174 | ||||
| -rw-r--r-- | os/cerf250/mkfile | 102 | ||||
| -rw-r--r-- | os/cerf250/uart.h | 118 |
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 IC (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, ðer->mbps, ðer->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) +{ +} |
