diff options
Diffstat (limited to 'os/mpc')
36 files changed, 12867 insertions, 0 deletions
diff --git a/os/mpc/800io.h b/os/mpc/800io.h new file mode 100644 index 00000000..c67ced41 --- /dev/null +++ b/os/mpc/800io.h @@ -0,0 +1,666 @@ +typedef struct BD BD; +typedef struct CPMdev CPMdev; +typedef struct GTimer GTimer; +typedef struct I2Cdev I2Cdev; +typedef struct PCMconftab PCMconftab; +typedef struct PCMmap PCMmap; +typedef struct PCMslot PCMslot; +typedef struct Ring Ring; + +/* + * MPC800 series IO structures + */ + +enum +{ + /* interrupt vectors (SIU and CPM) */ + VectorPIC= 0, /* level 0 to level 7, assigned by software */ + /* vector assignments are determined by the assignments here */ + PITlevel= 2, + CPIClevel= 4, + PCMCIAio= 5, + PCMCIAstatus= 6, + RTClevel= 7, + VectorIRQ= VectorPIC+8, /* IRQ0 to IRQ7 */ + VectorCPIC= VectorIRQ+8, /* 32 CPM interrupts: 0 (error) to 0x1F (PC15) */ + MaxVector= VectorCPIC+32, +}; + +/* + * these are defined to keep the interface compatible with other + * architectures, but only BUSUNKNOWN is currently used + */ +#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) +#define BUSFNO(tbdf) (((tbdf)>>8)&0x07) +#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) +#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) +#define BUSTYPE(tbdf) ((tbdf)>>24) +#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) +#define BUSUNKNOWN (-1) + +/* + * Buffer Descriptors and IO Rings + */ + +struct BD { + ushort status; + ushort length; + ulong addr; +}; + +BD* bdalloc(int); +void bdfree(BD*, int); +void dumpbd(char*, BD*, int); + +enum { + /* Rx BDs, bits common to all protocols */ + BDEmpty= 1<<15, + BDWrap= 1<<13, + BDInt= 1<<12, + BDLast= 1<<11, + BDFirst= 1<<10, + + /* Tx BDs */ + BDReady= 1<<15, + /* BDWrap, BDInt, BDLast */ +}; + + +struct Ring { + BD* rdr; /* receive descriptor ring */ + void* rrb; /* receive ring buffers */ + int rdrx; /* index into rdr */ + int nrdre; /* length of rdr */ + + BD* tdr; /* transmit descriptor ring */ + Block** txb; /* corresponding transmit ring buffers */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntdre; /* length of tdr */ + int ntq; /* pending transmit requests */ +}; + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + +int ioringinit(Ring*, int, int, int); + +/* + * CPM + */ +enum { + /* commands */ + InitRxTx = 0, + InitRx = 1, + InitTx = 2, + EnterHunt= 3, + StopTx= 4, + GracefulStopTx = 5, + InitIDMA = 5, + RestartTx = 6, + CloseRxBD = 7, + SetGroupAddr = 8, + SetTimer = 8, + GCITimeout = 9, + GCIAbort = 10, + StopIDMA = 11, + StartDSP = 12, + ArmIDMA = 13, + InitDSP = 13, + USBCmd = 15, + + /* bgcr */ + BaudEnable = 1<<16, + + /* sicr */ + CLK1 = 4, /* SCC1,2 */ + CLK2 = 5, + CLK3 = 6, + CLK4 = 7, + CLK5 = CLK1, /* SCC3,4 */ + CLK6 = CLK2, + CLK7 = CLK3, + CLK8 = CLK4, + + /* logical channel IDs mapped to channel ID by cpm.c */ + CPnone = 0, + CPscc1, + CPscc2, + CPscc3, + CPscc4, + CPsmc1, + CPsmc2, + CPdsp1, + CPdsp2, + CPidma1, + CPidma2, + CPtimer, + CPspi, + CPi2c, + CPmax, +}; + +struct CPMdev { + int id; /* CPM channel number */ + int irq; /* CPIC interrupt number */ + int rbase; /* register offset in IO mem */ + int pbase; /* parameter offset in IO mem */ + void* regs; /* kernel address of registers */ + void* param; /* kernel address of parameters */ +}; + +CPMdev* cpmdev(int); +void cpmop(CPMdev*, int, int); +void* cpmalloc(int, int); +void cpmfree(void*, int); +IMM* ioplock(void); +void iopunlock(void); + +int cpmidopen(int, void*); +void cpmidclose(int); +void sccnmsi(int, int, int); +void sccxstop(CPMdev*); +void smcnmsi(int, int); +void smcxstop(CPMdev*); + +/* + * CPM timers + */ +enum { + /* timer modes */ + CaptureRise= 1<<6, + CaptureFall= 2<<6, + CaptureEdge= 3<<6, + TimerToggle= 1<<5, /* toggle TOUTx* pin */ + TimerORI= 1<<4, /* Output Reference Interrupt */ + TimerRestart= 1<<3, + TimerSclk= 1<<1, + TimerSclk16= 2<<1, + TimerTIN= 3<<1, /* clock by falling edge of TINx */ + TimerGate= 1<<0, /* TGATE1* controls timer */ + + /* timer events */ + TimerREF= 1<<1, + TimerCAP= 1<<0 +}; + +struct GTimer{ + int x; + int inuse; + int event; + ushort* tmr; + ushort* trr; + ushort* tcr; + ushort* tcn; + ushort* ter; + void* arg; + void (*interrupt)(Ureg*, void*, GTimer*); +}; +GTimer* gtimer(ushort, ushort, void (*)(Ureg*,void*,GTimer*), void*); +void gtimerset(GTimer*, ushort, int); +void gtimerstart(GTimer*); +void gtimerstop(GTimer*); +void gtimerfree(GTimer*); + +/* + * the structures below follow hardware/firmware layouts in the 8xx manuals: + * mind the data types, offsets and alignment + */ + +/* + * basic IO controller parameters (SMC and SCC) + */ +typedef struct IOCparam IOCparam; +struct IOCparam { + ushort rbase; + ushort tbase; + uchar rfcr; + uchar tfcr; + ushort mrblr; + ulong rstate; + ulong rptr; + ushort rbptr; + ushort rcnt; + ulong rtmp; + ulong tstate; + ulong tptr; + ushort tbptr; + ushort tcnt; + ulong ttmp; +}; + +typedef struct SCCparam SCCparam; +struct SCCparam { + IOCparam; + ulong rcrc; + ulong tcrc; +}; + +typedef struct SCC SCC; +struct SCC { + ulong gsmrl; + ulong gsmrh; + ushort psmr; + uchar rsvscc0[2]; + ushort todr; + ushort dsr; + ushort scce; + uchar rsvscc1[2]; + ushort sccm; + uchar rsvscc3; + uchar sccs; + ushort irmode; + ushort irsip; +}; + +typedef struct SMC SMC; +struct SMC { + uchar pad1[2]; + ushort smcmr; + uchar pad2[2]; + uchar smce; + uchar pad3[3]; + uchar smcm; + uchar pad4[5]; +}; + +typedef struct SPI SPI; +struct SPI { + ushort spmode; + uchar res1[4]; + uchar spie; + uchar res2[3]; + uchar spim; + uchar res3[2]; + uchar spcom; + uchar res4[10]; +}; + +typedef struct USB USB; +struct USB { /* 823 only */ + uchar usmod; + uchar usadr; + uchar uscom; + uchar rsvu1; + ushort usep[4]; + uchar rsvu2[4]; + ushort usber; + uchar rsvu3[2]; + ushort usbmr; + uchar rsvu4; + uchar usbs; + uchar rsvu5[8]; +}; + +typedef struct IMM IMM; +struct IMM { + struct { /* general SIU */ + ulong siumcr; + ulong sypcr; + uchar rsv0[0xE-0x8]; + ushort swsr; + ulong sipend; + ulong simask; + ulong siel; + uchar sivec; + uchar padv[3]; + ulong tesr; + uchar rsv1[0x30-0x24]; + ulong sdcr; + uchar rsv2[0x80-0x34]; + }; + struct { /* PCMCIA */ + struct { + ulong base; + ulong option; + } pcmr[8]; + uchar rsv3[0xe0-0xc0]; + ulong pgcr[2]; + ulong pscr; + uchar rsv4[0xf0-0xec]; + ulong pipr; + uchar rsv5[4]; + ulong per; + uchar rsv6[4]; + }; + struct { /* MEMC */ + struct { + ulong base; + ulong option; + } memc[8]; + uchar rsv7a[0x24]; + ulong mar; + ulong mcr; + uchar rsv7b[4]; + ulong mamr; + ulong mbmr; + ushort mstat; + ushort mptpr; + ulong mdr; + uchar rsv7c[0x80]; + }; + struct { /* system integration timers */ + ushort tbscr; + uchar rsv8a[2]; + ulong tbrefu; + ulong tbrefl; + uchar rsv8b[0x14]; + ushort rtcsc; + uchar rsv8c[2]; + ulong rtc; + ulong rtsec; + ulong rtcal; + uchar rsv8d[0x10]; + ushort piscr; + ushort rsv8e; + ulong pitc; + ulong pitr; + uchar rsv8f[0x34]; + }; + struct { /* 280: clocks and resets */ + ulong sccr; + ulong plprcr; + ulong rsr; + uchar rsv9[0x300-0x28c]; + }; + struct { /* 300: system integration timers keys */ + ulong tbscrk; + ulong tbrefuk; + ulong tbreflk; + ulong tbk; + uchar rsv10a[0x10]; + ulong rtcsck; + ulong rtck; + ulong rtseck; + ulong rtcalk; + uchar rsv10b[0x10]; + ulong piscrk; + ulong pitck; + uchar rsv10c[0x38]; + }; + struct { /* 380: clocks and resets keys */ + ulong sccrk; + ulong plprcrk; + ulong rsrk; + uchar rsv11[0x800-0x38C]; + }; + struct { /* 800: video controller */ + ushort vccr; + ushort pad11a; + uchar vsr; + uchar pad11b; + uchar vcmr; + uchar pad11c; + ulong vbcb; + ulong pad11d; + ulong vfcr0; + ulong vfaa0; + ulong vfba0; + ulong vfcr1; + ulong vfaa1; + ulong vfba1; + uchar rsv11a[0x840-0x828]; + }; + struct { /* 840: LCD */ + ulong lccr; + ulong lchcr; + ulong lcvcr; + ulong rsv11b; + ulong lcfaa; + ulong lcfba; + uchar lcsr; + uchar rsv11c[0x860-0x859]; + }; + struct { /* 860: I2C */ + uchar i2mod; + uchar rsv12a[3]; + uchar i2add; + uchar rsv12b[3]; + uchar i2brg; + uchar rsv12c[3]; + uchar i2com; + uchar rsv12d[3]; + uchar i2cer; + uchar rsv12e[3]; + uchar i2cmr; + uchar rsv12[0x900-0x875]; + }; + struct { /* 900: DMA */ + uchar rsv13[4]; + ulong sdar; + uchar sdsr; + uchar pad1[3]; + uchar sdmr; + uchar pad2[3]; + uchar idsr1; + uchar pad3[3]; + uchar idmr1; + uchar pad4[3]; + uchar idsr2; + uchar pad5[3]; + uchar idmr2; + uchar pad6[0x930-0x91D]; + }; + struct { /* CPM interrupt control */ + ushort civr; + uchar pad7[0x940-0x932]; + ulong cicr; + ulong cipr; + ulong cimr; + ulong cisr; + }; + struct { /* input/output port */ + ushort padir; + ushort papar; + ushort paodr; + ushort padat; + uchar pad8[8]; + ushort pcdir; + ushort pcpar; + ushort pcso; + ushort pcdat; + ushort pcint; + uchar pad9[6]; + ushort pddir; + ushort pdpar; + ushort rsv14a; + ushort pddat; + uchar rsv14[0x980-0x978]; + }; + struct { /* CPM timers */ + ushort tgcr; + uchar rsv15a[0x990-0x982]; + ushort tmr1; + ushort tmr2; + ushort trr1; + ushort trr2; + ushort tcr1; + ushort tcr2; + ushort tcn1; + ushort tcn2; + ushort tmr3; + ushort tmr4; + ushort trr3; + ushort trr4; + ushort tcr3; + ushort tcr4; + ushort tcn3; + ushort tcn4; + ushort ter1; + ushort ter2; + ushort ter3; + ushort ter4; + uchar rsv15[0x9C0-0x9B8]; + }; + struct { /* CPM */ + ushort cpcr; + uchar res0[2]; + ushort rccr; + uchar res1; + uchar rmds; + uchar res2a[4]; + ushort rctr1; + ushort rctr2; + ushort rctr3; + ushort rctr4; + uchar res2[2]; + ushort rter; + uchar res3[2]; + ushort rtmr; + uchar rsv16[0x9F0-0x9DC]; + }; + union { /* BRG */ + struct { + ulong brgc1; + ulong brgc2; + ulong brgc3; + ulong brgc4; + }; + ulong brgc[4]; + }; + uchar skip0[0xAB2-0xA00]; /* USB, SCC, SMC, SPI: address using cpmdev(CP...)->regs */ + struct { /* PIP */ + ushort pipc; /* not 823 */ + ushort ptpr; /* not 823 */ + ulong pbdir; + ulong pbpar; + uchar pad10[2]; + ushort pbodr; + ulong pbdat; + uchar pad11[0xAE0-0xAC8]; + }; + struct { /* SI */ + ulong simode; + uchar sigmr; + uchar pad12; + uchar sistr; + uchar sicmr; + uchar pad13[4]; + ulong sicr; + ulong sirp; + uchar pad14[0xB00-0xAF4]; + }; + ulong vcram[64]; + ushort siram[256]; + ushort lcdmap[256]; +}; + +/* + * PCMCIA structures known by both ../port/cis.c and the pcmcia driver + */ + +/* + * Map between physical memory space and PCMCIA card memory space. + */ +struct PCMmap { + ulong ca; /* card address */ + ulong cea; /* card end address */ + ulong isa; /* local virtual address */ + int len; /* length of the ISA area */ + int attr; /* attribute memory */ + int slotno; /* owning slot */ + int ref; +}; + +/* + * a PCMCIA configuration entry + */ +struct PCMconftab +{ + int index; + ushort irqs; /* legal irqs */ + uchar irqtype; + uchar bit16; /* true for 16 bit access */ + uchar nlines; + struct { + ulong start; + ulong len; + PCMmap* map; + } io[16]; + int nio; + int vcc; + int vpp1; + int vpp2; + uchar memwait; + ulong maxwait; + ulong readywait; + ulong otherwait; +}; + +/* + * PCMCIA card slot + */ +struct PCMslot +{ +// RWlock; + +// Ref ref; +Ref; + + void* ctlr; /* controller for this slot */ + + long memlen; /* memory length */ + uchar slotno; /* slot number */ + uchar slotshift; /* >> register to meet mask; << mask to meet register */ + void *regs; /* i/o registers */ + void *mem; /* memory */ + void *attr; /* attribute memory */ + + /* status */ + uchar occupied; /* card in the slot */ + uchar configed; /* card configured */ + uchar busy; + uchar powered; + uchar battery; + uchar wrprot; + uchar enabled; + uchar special; + uchar dsize; + uchar v3_3; + uchar voltage; + + /* cis info */ + int cisread; /* set when the cis has been read */ + char verstr[512]; /* version string */ + uchar cpresent; /* config registers present */ + ulong caddr; /* relative address of config registers */ + int nctab; /* number of config table entries */ + PCMconftab ctab[8]; + PCMconftab *def; /* default conftab */ + + /* maps are fixed */ + PCMmap memmap; + PCMmap attrmap; + + struct { + void (*f)(Ureg*, void*); + void *arg; + } intr; + struct { + void (*f)(void*, int); + void *arg; + } notify; +}; + +/* ../port/cis.c */ +void pcmcisread(PCMslot*); +int pcmcistuple(int, int, int, void*, int); + +/* devpcmcia.c */ +PCMmap* pcmmap(int, ulong, int, int); +void pcmunmap(int, PCMmap*); + +/* + * used by ../port/devi2c.c and i2c.c + */ +struct I2Cdev { + int addr; + int salen; /* length in bytes of subaddress, if used; 0 otherwise */ + int tenbit; /* 10-bit addresses */ +}; + +long i2crecv(I2Cdev*, void*, long, ulong); +long i2csend(I2Cdev*, void*, long, ulong); +void i2csetup(int); diff --git a/os/mpc/NOTICE b/os/mpc/NOTICE new file mode 100644 index 00000000..1ba3456b --- /dev/null +++ b/os/mpc/NOTICE @@ -0,0 +1,3 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +PowerPC support Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net). All rights reserved. +MPC8xx Inferno PowerPC port Copyright © 1998-2003 Vita Nuova Holdings Limited. All rights reserved. diff --git a/os/mpc/clock.c b/os/mpc/clock.c new file mode 100644 index 00000000..f8ed362f --- /dev/null +++ b/os/mpc/clock.c @@ -0,0 +1,145 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +#include <isa.h> +#include <interp.h> + +typedef struct Clock0link Clock0link; +typedef struct Clock0link { + void (*clock)(void); + Clock0link* link; +} Clock0link; + +static Clock0link *clock0link; +static Lock clock0lock; +ulong clkrelinq; +void (*kproftick)(ulong); /* set by devkprof.c when active */ +void (*archclocktick)(void); /* set by arch*.c when desired */ + +Timer* +addclock0link(void (*clock)(void), int) +{ + Clock0link *lp; + + if((lp = malloc(sizeof(Clock0link))) == 0){ + print("addclock0link: too many links\n"); + return nil; + } + ilock(&clock0lock); + lp->clock = clock; + lp->link = clock0link; + clock0link = lp; + iunlock(&clock0lock); + return nil; +} + +void +delay(int l) +{ + ulong i, j; + + j = m->delayloop; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +void +microdelay(int l) +{ + ulong i; + + l *= m->delayloop; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +enum { + Timebase = 4, /* system clock cycles per time base cycle */ +}; + +static ulong clkreload; + +void +clockinit(void) +{ + long x; + + m->delayloop = m->cpuhz/1000; /* initial estimate */ + do { + x = gettbl(); + delay(10); + x = gettbl() - x; + } while(x < 0); + + /* + * fix count + */ + m->delayloop = ((vlong)m->delayloop*(10*m->clockgen/1000))/(x*Timebase); + if(m->delayloop == 0) + m->delayloop = 1; + + clkreload = (m->clockgen/Timebase)/HZ-1; + putdec(clkreload); +} + +void +clockintr(Ureg *ur) +{ + Clock0link *lp; + long v; + + v = -getdec(); + if(v > clkreload/2){ + if(v > clkreload) + m->ticks += v/clkreload; + v = 0; + } + putdec(clkreload-v); + + /* watchdog */ + if(m->iomem->sypcr & (1<<2)){ + m->iomem->swsr = 0x556c; + m->iomem->swsr = 0xaa39; + } + + m->ticks++; + if(archclocktick != nil) + archclocktick(); + + if(up) + up->pc = ur->pc; + + checkalarms(); + if(m->machno == 0) { + if(kproftick != nil) + (*kproftick)(ur->pc); + if(canlock(&clock0lock)){ + for(lp = clock0link; lp; lp = lp->link) + lp->clock(); + unlock(&clock0lock); + } + } + + if(up && up->state == Running){ + if(cflag && up->type == Interp && tready(nil)) + ur->cr |= 1; /* set flag in condition register for ../../libinterp/comp-power.c:/^schedcheck */ + } + /* other preemption checks are done by trap.c */ +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} diff --git a/os/mpc/cpm.c b/os/mpc/cpm.c new file mode 100644 index 00000000..e985d947 --- /dev/null +++ b/os/mpc/cpm.c @@ -0,0 +1,695 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +typedef struct Chanuse Chanuse; +struct Chanuse { + Lock; + void* owner; +} ; + +enum { + BDSIZE= 1024, /* IO memory reserved for buffer descriptors */ + CPMSIZE= 1024, /* IO memory reserved for other uses */ + + /* channel IDs */ + SCC1ID= 0, + I2CID= 1, + IDMA1ID= 1, + SCC2ID= 4, + SPIID= 5, + IDMA2ID= 5, + TIMERID= 5, + SCC3ID= 8, + SMC1ID= 9, + DSP1ID= 9, + SCC4ID= 12, + SMC2ID= 13, + DSP2ID= 13, + NCPMID= 16, + + NSCC = 4, + + /* SCC.gsmr_l */ + ENR = 1<<5, /* enable receiver */ + ENT = 1<<4, /* enable transmitter */ + + NSMC = 2, + + /* SMC.smcmr */ + TEN = 1<<1, /* transmitter enable */ + REN = 1<<0, /* receiver enable */ +}; + +static Map bdmapv[BDSIZE/sizeof(BD)]; +static RMap bdmap = {"buffer descriptors"}; + +static Map cpmmapv[CPMSIZE/sizeof(ulong)]; +static RMap cpmmap = {"CPM memory"}; + +static Lock cpmlock; + +static struct { + Lock; + ulong avail; +} brgens; + +static Chanuse cpmids[NCPMID]; +static CPMdev cpmdevinfo[] = { + [CPscc1] {SCC1ID, 0x1E, 0xA00, 0x3C00}, + [CPscc2] {SCC2ID, 0x1D, 0xA20, 0x3D00}, + [CPscc3] {SCC3ID, 0x1C, 0xA40, 0x3E00}, + [CPscc4] {SCC4ID, 0x1B, 0xA60, 0x3F00}, + [CPsmc1] {SMC1ID, 0x04, 0xA80, 0x3E80}, + [CPsmc2] {SMC2ID, 0x03, 0xA90, 0x3F80}, + [CPdsp1] {DSP1ID, 0x16, 0, 0x3EC0}, + [CPdsp2] {DSP2ID, 0x16, 0, 0x3FC0}, + [CPidma1] {IDMA1ID, 0x15, 0, 0x3CC0}, + [CPidma2] {IDMA2ID, 0x14, 0, 0x3DC0}, + [CPtimer] {TIMERID, 0x11, 0, 0x3DB0}, + [CPspi] {SPIID, 0x05, 0xAA0, 0x3D80}, /* parameters relocated below */ + [CPi2c] {I2CID, 0x10, 0x860, 0x3C80}, /* parameters relocated below */ +}; + +static void i2cspireloc(void); +static void* relocateparam(ulong, int); + +/* + * initialise the communications processor module + * and associated device registers + */ +void +cpminit(void) +{ + IMM *io; + + io = m->iomem; + io->sdcr = 1; + io->rccr = 0; + io->rmds = 0; + io->lccr = 0; /* disable LCD */ + io->vccr = 0; /* disable video */ + io->i2mod = 0; /* stop I2C */ + io->pcint = 0; /* disable all port C interrupts */ + io->pcso = 0; + io->pcdir =0; + io->pcpar = 0; + io->pcdat = 0; + io->papar = 0; + io->padir = 0; + io->paodr = 0; + io->padat = 0; + io->pbpar = 0; + io->pbdir = 0; + io->pbodr = 0; + io->pbdat = 0; + io->tgcr = 0x2222; /* reset timers, low-power stop */ + eieio(); + + for(io->cpcr = 0x8001; io->cpcr & 1;) /* reset all CPM channels */ + eieio(); + + mapinit(&bdmap, bdmapv, sizeof(bdmapv)); + mapfree(&bdmap, DPBASE, BDSIZE); + mapinit(&cpmmap, cpmmapv, sizeof(cpmmapv)); + mapfree(&cpmmap, DPBASE+BDSIZE, CPMSIZE); + + if(m->cputype == 0x50 && (getimmr() & 0xFFFF) <= 0x2001) + brgens.avail = 0x3; + else + brgens.avail = 0xF; + i2cspireloc(); +} + +/* + * return parameters defining a CPM device, given logical ID + */ +CPMdev* +cpmdev(int n) +{ + CPMdev *d; + + if(n < 0 || n >= nelem(cpmdevinfo)) + panic("cpmdev"); + d = &cpmdevinfo[n]; + if(d->param == nil && d->pbase != 0){ + if((n == CPi2c || n == CPspi)){ + d->param = relocateparam(d->pbase, 0xB0-0x80); /* relocate */ + if(d->param == nil) + return nil; + } else + d->param = (char*)m->iomem+d->pbase; + } + if(d->rbase != 0) + d->regs = (char*)m->iomem+d->rbase; + return d; +} + +/* + * issue a request to a CPM device + */ +void +cpmop(CPMdev *cpd, int op, int param) +{ + IMM *io; + + ilock(&cpmlock); + io = m->iomem; + while(io->cpcr & 1) + eieio(); + io->cpcr = (op<<8)|(cpd->id<<4)|(param<<1)|1; + eieio(); + while(io->cpcr & 1) + eieio(); + iunlock(&cpmlock); +} + +/* + * lock the shared IO memory and return a reference to it + */ +IMM* +ioplock(void) +{ + ilock(&cpmlock); + return m->iomem; +} + +/* + * release the lock on the shared IO memory + */ +void +iopunlock(void) +{ + eieio(); + iunlock(&cpmlock); +} + +/* + * connect SCCx clocks in NSMI mode (x=1 for USB) + */ +void +sccnmsi(int x, int rcs, int tcs) +{ + IMM *io; + ulong v; + int sh; + + sh = (x-1)*8; /* each SCCx field in sicr is 8 bits */ + v = (((rcs&7)<<3) | (tcs&7)) << sh; + io = ioplock(); + io->sicr = (io->sicr & ~(0xFF<<sh)) | v; + iopunlock(); +} + +/* + * connect SMCx clock in NSMI mode + */ +void +smcnmsi(int x, int cs) +{ + IMM *io; + ulong v; + int sh; + + if(x == 1) + sh = 0; + else + sh = 16; + v = cs << (12+sh); + io = ioplock(); + io->simode = (io->simode & ~(0xF000<<sh)) | v; /* SMCx to NMSI mode, set Tx/Rx clock */ + iopunlock(); +} + +/* + * claim the use of a CPM ID (SCC, SMC) that might be used by two mutually exclusive devices, + * for the caller determined by the given parameter (which must be unique). + * returns non-zero if the resource is already in use. + */ +int +cpmidopen(int id, void *owner) +{ + Chanuse *use; + + use = &cpmids[id]; + ilock(use); + if(use->owner != nil && use->owner != owner){ + iunlock(use); + return -1; + } + use->owner = owner; + iunlock(use); + return 0; +} + +/* + * release a previously claimed CPM ID + */ +void +cpmidclose(int id) +{ + Chanuse *use; + + use = &cpmids[id]; + ilock(use); + use->owner = nil; + iunlock(use); +} + +/* + * if SCC d is currently enabled, shut it down + */ +void +sccxstop(CPMdev *d) +{ + SCC *scc; + + if(d == nil) + return; + scc = d->regs; + if(scc->gsmrl & (ENT|ENR)){ + if(scc->gsmrl & ENT) + cpmop(d, GracefulStopTx, 0); + if(scc->gsmrl & ENR) + cpmop(d, CloseRxBD, 0); + delay(1); + scc->gsmrl &= ~(ENT|ENR); /* disable current use */ + eieio(); + } + scc->sccm = 0; /* mask interrupts */ +} + +/* + * if SMC d is currently enabled, shut it down + */ +void +smcxstop(CPMdev *d) +{ + SMC *smc; + + if(d == nil) + return; + smc = d->regs; + if(smc->smcmr & (TEN|REN)){ + if(smc->smcmr & TEN) + cpmop(d, StopTx, 0); + if(smc->smcmr & REN) + cpmop(d, CloseRxBD, 0); + delay(1); + smc->smcmr &= ~(TEN|REN); + eieio(); + } + smc->smcm = 0; /* mask interrupts */ +} + +/* + * allocate a buffer descriptor + */ +BD * +bdalloc(int n) +{ + ulong a; + + a = rmapalloc(&bdmap, 0, n*sizeof(BD), sizeof(BD)); + if(a == 0) + panic("bdalloc"); + return KADDR(a); +} + +/* + * free a buffer descriptor + */ +void +bdfree(BD *b, int n) +{ + if(b){ + eieio(); + mapfree(&bdmap, PADDR(b), n*sizeof(BD)); + } +} + +/* + * print a buffer descriptor and its data (when debugging) + */ +void +dumpbd(char *name, BD *b, int maxn) +{ + uchar *d; + int i; + + print("%s #%4.4lux: s=#%4.4ux l=%ud a=#%8.8lux", name, PADDR(b)&0xFFFF, b->status, b->length, b->addr); + if(maxn > b->length) + maxn = b->length; + if(b->addr != 0){ + d = KADDR(b->addr); + for(i=0; i<maxn; i++) + print(" %2.2ux", d[i]); + if(i < b->length) + print(" ..."); + } + print("\n"); +} + +/* + * allocate memory from the shared IO memory space + */ +void * +cpmalloc(int n, int align) +{ + ulong a; + + a = rmapalloc(&cpmmap, 0, n, align); + if(a == 0) + panic("cpmalloc"); + return KADDR(a); +} + +/* + * free previously allocated shared memory + */ +void +cpmfree(void *p, int n) +{ + if(p != nil && n > 0){ + eieio(); + mapfree(&cpmmap, PADDR(p), n); + } +} + +/* + * allocate a baud rate generator, returning its index + * (or -1 if none is available) + */ +int +brgalloc(void) +{ + int n; + + lock(&brgens); + for(n=0; brgens.avail!=0; n++) + if(brgens.avail & (1<<n)){ + brgens.avail &= ~(1<<n); + unlock(&brgens); + return n; + } + unlock(&brgens); + return -1; +} + +/* + * free a previously allocated baud rate generator + */ +void +brgfree(int n) +{ + if(n >= 0){ + if(n > 3 || brgens.avail & (1<<n)) + panic("brgfree"); + lock(&brgens); + brgens.avail |= 1 << n; + unlock(&brgens); + } +} + +/* + * return a value suitable for loading into a baud rate + * generator to produce the given rate if the generator + * is prescaled by the given amount (typically 16). + * the value must be or'd with BaudEnable to start the generator. + */ +ulong +baudgen(int rate, int scale) +{ + int d; + + rate *= scale; + d = (2*m->cpuhz+rate)/(2*rate) - 1; + if(d < 0) + d = 0; + if(d >= (1<<12)) + return ((d+15)>>(4-1))|1; /* divider too big: enable prescale by 16 */ + return d<<1; +} + +/* + * initialise receive and transmit buffer rings. + */ +int +ioringinit(Ring* r, int nrdre, int ntdre, int bufsize) +{ + int i, x; + + /* the ring entries must be aligned on sizeof(BD) boundaries */ + r->nrdre = nrdre; + if(r->rdr == nil) + r->rdr = bdalloc(nrdre); + /* the buffer size must align with cache lines since the cache doesn't snoop */ + bufsize = (bufsize+CACHELINESZ-1)&~(CACHELINESZ-1); + if(r->rrb == nil) + r->rrb = malloc(nrdre*bufsize); + if(r->rdr == nil || r->rrb == nil) + return -1; + dcflush(r->rrb, nrdre*bufsize); + x = PADDR(r->rrb); + for(i = 0; i < nrdre; i++){ + r->rdr[i].length = 0; + r->rdr[i].addr = x; + r->rdr[i].status = BDEmpty|BDInt; + x += bufsize; + } + r->rdr[i-1].status |= BDWrap; + r->rdrx = 0; + + r->ntdre = ntdre; + if(r->tdr == nil) + r->tdr = bdalloc(ntdre); + if(r->txb == nil) + r->txb = malloc(ntdre*sizeof(Block*)); + if(r->tdr == nil || r->txb == nil) + return -1; + for(i = 0; i < ntdre; i++){ + r->txb[i] = nil; + r->tdr[i].addr = 0; + r->tdr[i].length = 0; + r->tdr[i].status = 0; + } + r->tdr[i-1].status |= BDWrap; + r->tdrh = 0; + r->tdri = 0; + r->ntq = 0; + return 0; +} + +/* + * Allocate a new parameter block for I2C or SPI, + * and plant a pointer to it for the microcode, returning the kernel address. + * See Motorola errata and microcode package: + * the design botch is that the parameters for the SCC2 ethernet overlap the + * SPI/I2C parameter space; this compensates by relocating the latter. + * This routine may be used iff i2cspireloc is used (and it is, above). + */ +static void* +relocateparam(ulong olda, int nb) +{ + void *p; + + if(olda < (ulong)m->iomem) + olda += (ulong)m->iomem; + p = cpmalloc(nb, 32); /* ``RPBASE must be multiple of 32'' */ + if(p == nil) + return p; + *(ushort*)KADDR(olda+0x2C) = PADDR(p); /* set RPBASE */ + eieio(); + return p; +} + +/* + * I2C/SPI microcode package from Motorola + * (to relocate I2C/SPI parameters), which was distributed + * on their web site in S-record format. + * + * May 1998 + */ + +/*S00600004844521B*/ +static ulong ubase1 = 0x2000; +static ulong ucode1[] = { + /* #02202000 */ 0x7FFFEFD9, + /* #02202004 */ 0x3FFD0000, + /* #02202008 */ 0x7FFB49F7, + /* #0220200C */ 0x7FF90000, + /* #02202010 */ 0x5FEFADF7, + /* #02202014 */ 0x5F89ADF7, + /* #02202018 */ 0x5FEFAFF7, + /* #0220201C */ 0x5F89AFF7, + /* #02202020 */ 0x3A9CFBC8, + /* #02202024 */ 0xE7C0EDF0, + /* #02202028 */ 0x77C1E1BB, + /* #0220202C */ 0xF4DC7F1D, + /* #02202030 */ 0xABAD932F, + /* #02202034 */ 0x4E08FDCF, + /* #02202038 */ 0x6E0FAFF8, + /* #0220203C */ 0x7CCF76CF, + /* #02202040 */ 0xFD1FF9CF, + /* #02202044 */ 0xABF88DC6, + /* #02202048 */ 0xAB5679F7, + /* #0220204C */ 0xB0937383, + /* #02202050 */ 0xDFCE79F7, + /* #02202054 */ 0xB091E6BB, + /* #02202058 */ 0xE5BBE74F, + /* #0220205C */ 0xB3FA6F0F, + /* #02202060 */ 0x6FFB76CE, + /* #02202064 */ 0xEE0DF9CF, + /* #02202068 */ 0x2BFBEFEF, + /* #0220206C */ 0xCFEEF9CF, + /* #02202070 */ 0x76CEAD24, + /* #02202074 */ 0x90B2DF9A, + /* #02202078 */ 0x7FDDD0BF, + /* #0220207C */ 0x4BF847FD, + /* #02202080 */ 0x7CCF76CE, + /* #02202084 */ 0xCFEF7E1F, + /* #02202088 */ 0x7F1D7DFD, + /* #0220208C */ 0xF0B6EF71, + /* #02202090 */ 0x7FC177C1, + /* #02202094 */ 0xFBC86079, + /* #02202098 */ 0xE722FBC8, + /* #0220209C */ 0x5FFFDFFF, + /* #022020A0 */ 0x5FB2FFFB, + /* #022020A4 */ 0xFBC8F3C8, + /* #022020A8 */ 0x94A67F01, + /* #022020AC */ 0x7F1D5F39, + /* #022020B0 */ 0xAFE85F5E, + /* #022020B4 */ 0xFFDFDF96, + /* #022020B8 */ 0xCB9FAF7D, + /* #022020BC */ 0x5FC1AFED, + /* #022020C0 */ 0x8C1C5FC1, + /* #022020C4 */ 0xAFDD5FC3, + /* #022020C8 */ 0xDF9A7EFD, + /* #022020CC */ 0xB0B25FB2, + /* #022020D0 */ 0xFFFEABAD, + /* #022020D4 */ 0x5FB2FFFE, + /* #022020D8 */ 0x5FCE600B, + /* #022020DC */ 0xE6BB600B, + /* #022020E0 */ 0x5FCEDFC6, + /* #022020E4 */ 0x27FBEFDF, + /* #022020E8 */ 0x5FC8CFDE, + /* #022020EC */ 0x3A9CE7C0, + /* #022020F0 */ 0xEDF0F3C8, + /* #022020F4 */ 0x7F0154CD, + /* #022020F8 */ 0x7F1D2D3D, + /* #022020FC */ 0x363A7570, + /* #02202100 */ 0x7E0AF1CE, + /* #02202104 */ 0x37EF2E68, + /* #02202108 */ 0x7FEE10EC, + /* #0220210C */ 0xADF8EFDE, + /* #02202110 */ 0xCFEAE52F, + /* #02202114 */ 0x7D0FE12B, + /* #02202118 */ 0xF1CE5F65, + /* #0220211C */ 0x7E0A4DF8, + /* #02202120 */ 0xCFEA5F72, + /* #02202124 */ 0x7D0BEFEE, + /* #02202128 */ 0xCFEA5F74, + /* #0220212C */ 0xE522EFDE, + /* #02202130 */ 0x5F74CFDA, + /* #02202134 */ 0x0B627385, + /* #02202138 */ 0xDF627E0A, + /* #0220213C */ 0x30D8145B, + /* #02202140 */ 0xBFFFF3C8, + /* #02202144 */ 0x5FFFDFFF, + /* #02202148 */ 0xA7F85F5E, + /* #0220214C */ 0xBFFE7F7D, + /* #02202150 */ 0x10D31450, + /* #02202154 */ 0x5F36BFFF, + /* #02202158 */ 0xAF785F5E, + /* #0220215C */ 0xBFFDA7F8, + /* #02202160 */ 0x5F36BFFE, + /* #02202164 */ 0x77FD30C0, + /* #02202168 */ 0x4E08FDCF, + /* #0220216C */ 0xE5FF6E0F, + /* #02202170 */ 0xAFF87E1F, + /* #02202174 */ 0x7E0FFD1F, + /* #02202178 */ 0xF1CF5F1B, + /* #0220217C */ 0xABF80D5E, + /* #02202180 */ 0x5F5EFFEF, + /* #02202184 */ 0x79F730A2, + /* #02202188 */ 0xAFDD5F34, + /* #0220218C */ 0x47F85F34, + /* #02202190 */ 0xAFED7FDD, + /* #02202194 */ 0x50B24978, + /* #02202198 */ 0x47FD7F1D, + /* #0220219C */ 0x7DFD70AD, + /* #022021A0 */ 0xEF717EC1, + /* #022021A4 */ 0x6BA47F01, + /* #022021A8 */ 0x2D267EFD, + /* #022021AC */ 0x30DE5F5E, + /* #022021B0 */ 0xFFFD5F5E, + /* #022021B4 */ 0xFFEF5F5E, + /* #022021B8 */ 0xFFDF0CA0, + /* #022021BC */ 0xAFED0A9E, + /* #022021C0 */ 0xAFDD0C3A, + /* #022021C4 */ 0x5F3AAFBD, + /* #022021C8 */ 0x7FBDB082, + /* #022021CC */ 0x5F8247F8, +}; + +/*S00600004844521B*/ +static ulong ubase2 = 0x2F00; +static ulong ucode2[] = { + /* #02202F00 */ 0x3E303430, + /* #02202F04 */ 0x34343737, + /* #02202F08 */ 0xABF7BF9B, + /* #02202F0C */ 0x994B4FBD, + /* #02202F10 */ 0xBD599493, + /* #02202F14 */ 0x349FFF37, + /* #02202F18 */ 0xFB9B177D, + /* #02202F1C */ 0xD9936956, + /* #02202F20 */ 0xBBFDD697, + /* #02202F24 */ 0xBDD2FD11, + /* #02202F28 */ 0x31DB9BB3, + /* #02202F2C */ 0x63139637, + /* #02202F30 */ 0x93733693, + /* #02202F34 */ 0x193137F7, + /* #02202F38 */ 0x331737AF, + /* #02202F3C */ 0x7BB9B999, + /* #02202F40 */ 0xBB197957, + /* #02202F44 */ 0x7FDFD3D5, + /* #02202F48 */ 0x73B773F7, + /* #02202F4C */ 0x37933B99, + /* #02202F50 */ 0x1D115316, + /* #02202F54 */ 0x99315315, + /* #02202F58 */ 0x31694BF4, + /* #02202F5C */ 0xFBDBD359, + /* #02202F60 */ 0x31497353, + /* #02202F64 */ 0x76956D69, + /* #02202F68 */ 0x7B9D9693, + /* #02202F6C */ 0x13131979, + /* #02202F70 */ 0x79376935, +}; + +/* + * compensate for chip design botch by installing + * microcode to relocate I2C and SPI parameters away + * from the ethernet parameters + */ +static void +i2cspireloc(void) +{ + IMM *io; + static int done; + + if(done) + return; + io = m->iomem; + io->rccr &= ~3; + memmove((uchar*)m->iomem+ubase1, ucode1, sizeof(ucode1)); + memmove((uchar*)m->iomem+ubase2, ucode2, sizeof(ucode2)); + io->rctr1 = 0x802a; /* relocate SPI */ + io->rctr2 = 0x8028; /* relocate SPI */ + io->rctr3 = 0x802e; /* relocate I2C */ + io->rctr4 = 0x802c; /* relocate I2C */ + io->rccr |= 1; + done = 1; +} diff --git a/os/mpc/cpmtimer.c b/os/mpc/cpmtimer.c new file mode 100644 index 00000000..bda25dcf --- /dev/null +++ b/os/mpc/cpmtimer.c @@ -0,0 +1,179 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +enum { + Ntimer = 4 /* maximum allowed by hardware */ +}; + +static struct { + Lock; + int init; + int ntimer; /* actual timers on this chip revision */ + GTimer t[Ntimer]; +} cpmtimers; + +static uchar timerirq[] = {0x19, 0x12, 0x0C, 0x07}; + +static void gtimerinit(int, ushort*, ushort*); + +static void +gtimerreset(void) +{ + IMM *io; + int i; + + ilock(&cpmtimers); + if(!cpmtimers.init){ + if(m->cputype == 0x50 && (getimmr() & 0xFFFF) <= 0x2001) + cpmtimers.ntimer = 2; + else + cpmtimers.ntimer = Ntimer; + io = m->iomem; + io->tgcr = 0x2222; /* reset timers, low-power stop */ + for(i=0; i<cpmtimers.ntimer; i++) + gtimerinit(i, &io->tmr1+i, &io->ter1+i); + cpmtimers.init = 1; + } + iunlock(&cpmtimers); +} + +static void +gtimerintr(Ureg *ur, void *arg) +{ + GTimer *t; + + t = arg; + t->event = *t->ter; + *t->ter = t->event; + if(t->inuse && t->interrupt != nil) + t->interrupt(ur, t->arg, t); +} + +static void +gtimerinit(int i, ushort *tmr, ushort *ter) +{ + GTimer *t; + char name[KNAMELEN]; + + snprint(name, sizeof(name), "timer.%d", i); + t = &cpmtimers.t[i]; + t->x = i*4; /* field in tgcr */ + t->inuse = 0; + t->interrupt = nil; + t->tmr = tmr; + t->trr = tmr+2; + t->tcr = tmr+4; + t->tcn = tmr+6; + t->ter = ter; + intrenable(VectorCPIC+timerirq[i], gtimerintr, t, BUSUNKNOWN, name); +} + +GTimer* +gtimer(ushort mode, ushort ref, void (*intr)(Ureg*,void*,GTimer*), void *arg) +{ + GTimer *t; + int i; + + t = cpmtimers.t; + if(!cpmtimers.init) + gtimerreset(); + ilock(&cpmtimers); + for(i=0; ; i++){ + if(i >= cpmtimers.ntimer){ + iunlock(&cpmtimers); + return nil; + } + if(t->inuse == 0) + break; + t++; + } + t->inuse = 1; + t->interrupt = intr; + t->arg = arg; + m->iomem->tgcr &= ~(0xF<<t->x); /* reset */ + *t->tmr = mode; + *t->tcn = 0; + *t->trr = ref; + *t->ter = 0xFFFF; + iunlock(&cpmtimers); + return t; +} + +void +gtimerset(GTimer *t, ushort mode, int usec) +{ + ulong ref, ps; + int clk; + + if(usec <= 0) + return; + ref = usec*m->speed; + clk = mode & (3<<1); + if(ref >= 0x1000000 && clk == TimerSclk){ + mode = (mode & ~clk) | TimerSclk16; + ref >>= 4; + } else if(clk == TimerSclk16) + ref >>= 4; + ps = (ref+(1<<16))/(1<<16); /* round up */ + ref /= ps; + *t->tmr = ((ps-1)<<8) | (mode&0xFF); + *t->trr = ref; +} + +void +gtimerstart(GTimer *t) +{ + if(t){ + ilock(&cpmtimers); + m->iomem->tgcr = (m->iomem->tgcr & ~(0xF<<t->x)) | (1<<t->x); /* enable */ + iunlock(&cpmtimers); + } +} + +void +gtimerstop(GTimer *t) +{ + if(t){ + ilock(&cpmtimers); + m->iomem->tgcr |= 2<<t->x; /* stop */ + iunlock(&cpmtimers); + } +} + +void +gtimerfree(GTimer *t) +{ + if(t){ + ilock(&cpmtimers); + t->inuse = 0; + *t->tmr = 0; /* disable interrupts */ + *t->ter = 0xFFFF; + m->iomem->tgcr = (m->iomem->tgcr & ~(0xF<<t->x)) | (2<<t->x); /* reset and stop */ + iunlock(&cpmtimers); + } +} + +#ifdef GTIMETEST +static void +gtintr(Ureg*, void*, GTimer*) +{ + m->bcsr[4] ^= DisableVideoLamp; /* toggle an LED */ +} + +void +gtimetest(void) +{ + GTimer *g; + + g = gtimer(0, 0, gtintr, nil); + gtimerset(g, TimerORI|TimerRestart|TimerSclk, 64000); + gtimerstart(g); + delay(1); +print("started timer: #%4.4ux #%4.4ux %8.8lux #%4.4ux #%4.4ux\n", *g->tmr, *g->trr, m->iomem->tgcr, *g->tcn, *g->ter); +print("ter=#%8.8lux tmr=#%8.8lux trr=#%8.8lux\n", g->ter, g->tmr, g->trr); +} +#endif diff --git a/os/mpc/devata.c b/os/mpc/devata.c new file mode 100644 index 00000000..69665e38 --- /dev/null +++ b/os/mpc/devata.c @@ -0,0 +1,1194 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "pcmcia.h" + +#define DPRINT if(0)print + +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, + + Maxxfer= BY2PG, /* maximum transfer size/cmd */ + Npart= 8+2, /* 8 sub partitions, disk, and partition */ + Nrepl= 64, /* maximum replacement blocks */ +}; +#define PART(x) ((x)&0xF) +#define DRIVE(x) (((x)>>4)&0x7) +#define MKQID(d,p) (((d)<<4) | (p)) + +struct Partition +{ + ulong start; + ulong end; + char name[NAMELEN+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[NAMELEN]; + + 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, Dirtab*, int, int s, Dir *dirp) +{ + Qid qid; + int drive; + Drive *dp; + Partition *pp; + ulong l; + + qid.vers = 0; + 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 */ + + print("ata init\n"); + cp = malloc(sizeof(*cp)); + if (!cp) + error(Enomem); + + cp->port = Pbase; + cp->irq = 14; + + if((pcmslot = pcmspecial("SunDisk", cp)) < 0) { + print("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; + pcmintrenable(pcmslot, ataintr, cp); + + 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 int +atawalk(Chan *c, char *name) +{ + return devwalk(c, name, 0, 0, atagen); +} + +static void +atastat(Chan *c, char *dp) +{ + devstat(c, dp, 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.path == CHDIR) + 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.path == CHDIR) + 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 intereference 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 %ld\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=%lux\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 %d>", (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%d err: lblk %ld status %lux, err %lux\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%d", 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], NAMELEN); + break; + case 3: + strncpy(pp->name, field[0], NAMELEN); + if(strncmp(pp->name, "repl", NAMELEN) == 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=%lux status=%lux\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=%lux status=%lux\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=%lux status=%lux\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, + ataattach, + devdetach, + devclone, + atawalk, + atastat, + ataopen, + devcreate, + ataclose, + ataread, + devbread, + atawrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/mpc/devbench.c b/os/mpc/devbench.c new file mode 100644 index 00000000..a35a57b0 --- /dev/null +++ b/os/mpc/devbench.c @@ -0,0 +1,243 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +typedef struct Psync Psync; + +enum { + Maxprocs=2, +}; + +struct Psync { + Rendez r; + int flag; +}; +static Psync timesync[Maxprocs]; +static Ref nactive; +static Ref nbusy; + +static int +timev(void *a) +{ + return *(int*)a; +} + +static void +timesched0(void *ap) +{ + long tot, t, i, lim, low, max; + Psync *ps; + + ps = ap; + sleep(&ps->r, timev, &ps->flag); + setpri(PriRealtime); + incref(&nbusy); + while(nbusy.ref < nactive.ref) + sched(); + lim = 1000; + low = 64000000; + max = 0; + tot = 0; + for(i=0; i<lim; i++){ +if(i<8)print("%lud\n", up->pid); + do{ + t = gettbl(); + sched(); + t = gettbl()-t; + }while(t < 0); + if(t < low) + low = t; + if(t > max) + max = t; + tot += t; + } + print("%lud %lud %lud %lud %lud\n", up->pid, lim, tot, low, max); + decref(&nactive); + pexit("", 0); +} + +static void +timesched(void) +{ + int i, np; + + for(np=1; np<=Maxprocs; np++){ + nactive.ref = np; + print("%d procs\n", np); + setpri(PriRealtime); + for(i=0; i<np; i++) + kproc("timesched", timesched0, ×ync[i], 0); + for(i=0; i<np; i++){ + timesync[i].flag = 1; + wakeup(×ync[i].r); + } + setpri(PriNormal); + while(nactive.ref>0) + sched(); + } +} + +typedef struct Ictr Ictr; +struct Ictr { + ulong base; + ulong sleep; + ulong spllo; + ulong intr; + ulong isave; + ulong arrive; + ulong wakeup; + ulong awake; +}; +static Ictr counters[100], *curct; +static int intrwant; +static Rendez vous; +int spltbl; /* set by spllo */ +int intrtbl; /* set by intrvec() */ +int isavetbl; /* set by intrvec() */ + +static void +intrwake(Ureg*, void*) +{ + m->iomem->tgcr &= ~1; /* reset the timer */ + curct->spllo = spltbl; + curct->intr = intrtbl; + curct->isave = isavetbl; + curct->arrive = gettbl(); + intrwant = 0; + wakeup(&vous); + curct->wakeup = gettbl(); +} + +/* + * sleep calls intrtest with splhi (under lock): + * provoke the interrupt now, so that it is guaranteed + * not to happen until sleep has queued the process, + * forcing wakeup to do something. + */ +static int +intrtest(void*) +{ + m->iomem->tgcr |= 1; /* enable timer: allow interrupt */ + curct->sleep = gettbl(); + return intrwant==0; +} + +static void +intrtime(void) +{ + IMM *io; + Ictr *ic; + long t; + int i; + + sched(); + curct = counters; + io = ioplock(); + io->tgcr &= ~3; + iopunlock(); + intrenable(VectorCPIC+0x19, intrwake, nil, BUSUNKNOWN, "bench"); + for(i=0; i<nelem(counters); i++){ + curct = &counters[i]; + //puttbl(0); + intrwant = 1; + io = m->iomem; /* don't lock, to save time */ + io->tmr1 = (0<<8)|TimerORI|TimerSclk; + io->trr1 = 1; + curct->base = gettbl(); + sleep(&vous, intrtest, nil); + curct->awake = gettbl(); + sched(); /* just to slow it down between trials */ + } + m->iomem->tmr1 = 0; + print("interrupt\n"); + for(i=0; i<20; i++){ + ic = &counters[i]; + t = ic->awake - ic->base; + ic->awake -= ic->wakeup; + ic->wakeup -= ic->arrive; + ic->arrive -= ic->isave; + ic->isave -= ic->intr; + ic->intr -= ic->spllo; + ic->spllo -= ic->sleep; + ic->sleep -= ic->base; + print("%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\n", ic->sleep, ic->spllo, ic->intr, ic->isave, ic->arrive, ic->wakeup, ic->awake, t); + } +} + +static Chan* +benchattach(char *spec) +{ + timesched(); + intrtime(); + USED(spec); + error(Eperm); + return nil; +} + +static Walkqid* +benchwalk(Chan*, Chan*, char**, int) +{ + error(Enonexist); + return 0; +} + +static Chan* +benchopen(Chan*, int) +{ + error(Eperm); + return nil; +} + +static int +benchstat(Chan*, uchar*, int) +{ + error(Eperm); + return 0; +} + +static void +benchclose(Chan*) +{ +} + +static long +benchread(Chan *c, void *buf, long n, vlong offset) +{ + USED(c, buf, n, offset); + error(Eperm); + return 0; +} + +static long +benchwrite(Chan *c, void *buf, long n, vlong offset) +{ + USED(c, buf, n, offset); + error(Eperm); + return 0; +} + + +Dev benchdevtab = { + 'x', + "bench", + + devreset, + devinit, + devshutdown, + benchattach, + benchwalk, + benchstat, + benchopen, + devcreate, + benchclose, + benchread, + devbread, + benchwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/mpc/devboot.c b/os/mpc/devboot.c new file mode 100644 index 00000000..126fe6e6 --- /dev/null +++ b/os/mpc/devboot.c @@ -0,0 +1,132 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum{ + Qdir, + Qboot, + Qmem, +}; + +Dirtab bootdir[]={ + ".", {Qdir,0,QTDIR}, 0, 0555, + "boot", {Qboot}, 0, 0666, + "mem", {Qmem}, 0, 0666, +}; + +#define NBOOT (sizeof bootdir/sizeof(Dirtab)) + +static void +bootreset(void) +{ +} + +static Chan* +bootattach(char *spec) +{ + return devattach('B', spec); +} + +static Walkqid* +bootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, bootdir, NBOOT, devgen); +} + +static int +bootstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, bootdir, NBOOT, devgen); +} + +static Chan* +bootopen(Chan *c, int omode) +{ + return devopen(c, omode, bootdir, NBOOT, devgen); +} + +static void +bootclose(Chan*) +{ +} + +static long +bootread(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + + switch((ulong)c->qid.path){ + + case Qdir: + return devdirread(c, buf, n, bootdir, NBOOT, devgen); + + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){ + if(offset+n > KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove(buf, (char*)offset, n); + return n; + } + error(Ebadarg); + } + + error(Egreg); + return 0; /* not reached */ +} + +static long +bootwrite(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + ulong pc; + uchar *p; + + switch((ulong)c->qid.path){ + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){ + if(offset+n > KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove((char*)offset, buf, n); + segflush((void*)offset, n); + return n; + } + error(Ebadarg); + + case Qboot: + p = (uchar*)buf; + pc = (((((p[0]<<8)|p[1])<<8)|p[2])<<8)|p[3]; + if(pc < KZERO || pc >= KZERO+conf.npage*BY2PG) + error(Ebadarg); + splhi(); + segflush((void*)pc, 64*1024); + gotopc(pc); + } + error(Ebadarg); + return 0; /* not reached */ +} + +Dev bootdevtab = { + 'B', + "boot", + + bootreset, + devinit, + devshutdown, + bootattach, + bootwalk, + bootstat, + bootopen, + devcreate, + bootclose, + bootread, + devbread, + bootwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/mpc/devether.c b/os/mpc/devether.c new file mode 100644 index 00000000..eb4ed283 --- /dev/null +++ b/os/mpc/devether.c @@ -0,0 +1,617 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + Ether *ether; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if((ether = etherxx[ctlrno]) == 0) + error(Enodev); + rlock(ether); + if(waserror()){ + runlock(ether); + nexterror(); + } + chan = devattach('l', spec); + chan->dev = ctlrno; + if(ether->attach) + ether->attach(etherxx[ctlrno]); + poperror(); + runlock(ether); + return chan; +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i=0; i<MaxEther; i++){ + ether = etherxx[i]; + if(ether != nil && ether->detach != nil) + ether->detach(ether); + } +} + +static Walkqid* +etherwalk(Chan* chan, Chan *nchan, char **name, int nname) +{ + Walkqid *wq; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname); + poperror(); + runlock(ether); + return wq; +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + int s; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + s = netifstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return s; +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + Chan *c; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + c = netifopen(ether, chan, omode); + poperror(); + runlock(ether); + return c; +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + netifclose(ether, chan); + poperror(); + runlock(ether); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + long r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid){ + r = ether->ifstat(ether, buf, n, offset); + goto out; + } + if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + r = netifread(ether, chan, buf, n, offset); +out: + poperror(); + runlock(ether); + return r; +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + Block *b; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + b = netifbread(ether, chan, n, offset); + poperror(); + runlock(ether); + return b; +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + Ether *ether; + int r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + r = netifwstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return r; +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multcast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if((f = *fp) && (f->type == type || f->type < 0)) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) + ether->soverflows++; + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + }else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int onoff; + Cmdbuf *cb; + long l; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(NETTYPE(chan->qid.path) != Ndataqid) { + l = netifwrite(ether, chan, buf, n); + if(l >= 0) + goto out; + cb = parsecmd(buf, n); + if(strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + goto out; + } + free(cb); + if(ether->ctl!=nil){ + l = ether->ctl(ether,buf,n); + goto out; + } + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += n; + poperror(); + + l = etheroq(ether, bp); +out: + poperror(); + runlock(ether); + return l; +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + n = etheroq(ether, bp); + poperror(); + runlock(ether); + return n; +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static void +etherreset(void) +{ + Ether *ether; + int i, n, ctlrno; + char name[KNAMELEN], buf[128]; + + for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if(ether == 0) + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + ether->tbdf = BUSUNKNOWN; + + if(archether(ctlrno, ether) <= 0) + continue; + + for(n = 0; cards[n].type; n++){ + if(cistrcmp(cards[n].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "ea=", 3) == 0){ + if(parseether(ether->ea, ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Eaddrlen); + }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 || + cistrcmp(ether->opt[i], "10BASE-TFD") == 0) + ether->fullduplex = 1; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + ether->mbps = 100; + } + if(cards[n].reset(ether)) + break; + snprint(name, sizeof(name), "ether%d", ctlrno); + + if(ether->interrupt != nil) + intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud", + ctlrno, ether->type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem)); + if(ether->size) + i += sprint(buf+i, " size 0x%luX", ether->size); + i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + print(buf); + + if(ether->mbps == 100){ + netifinit(ether, name, Ntypes, 256*1024); + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 64*1024); + if(ether->oq == 0) + ether->oq = qopen(64*1024, Qmsg, 0, 0); + } + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + etherxx[ctlrno] = ether; + ether = 0; + break; + } + } + if(ether) + free(ether); +} + +static void +etherpower(int on) +{ + int i; + Ether *ether; + + for(i = 0; i < MaxEther; i++){ + if((ether = etherxx[i]) == nil || ether->power == nil) + continue; + if(on){ + if(canrlock(ether)) + continue; + if(ether->power != nil) + ether->power(ether, on); + wunlock(ether); + }else{ + if(!canrlock(ether)) + continue; + wlock(ether); + if(ether->power != nil) + ether->power(ether, on); + /* Keep locked until power goes back on */ + } + } +} + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, + etherpower, +}; diff --git a/os/mpc/devpcmcia.c b/os/mpc/devpcmcia.c new file mode 100644 index 00000000..4c1d8311 --- /dev/null +++ b/os/mpc/devpcmcia.c @@ -0,0 +1,1076 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * MPC821/3 PCMCIA driver (prototype) + * + * unlike the i82365 adapter, there isn't an offset register: + * card addresses are simply the lower order 26 bits of the host address. + * + * to do: + * split allocation of memory/attrib (all 26 bits valid) and io space (typically 10 or 12 bits) + * correct config + * interrupts and i/o space access + * DMA? + * power control + */ + +enum +{ + Maxctlr= 1, + Maxslot= 2, + Slotashift= 16, + + /* pipr */ + Cbvs1= 1<<15, + Cbvs2= 1<<14, + Cbwp= 1<<13, + Cbcd2= 1<<12, + Cbcd1= 1<<11, + Cbbvd2= 1<<10, + Cbbvd1= 1<<9, + Cbrdy= 1<<8, + + /* pscr and per */ + Cbvs1_c= 1<<15, + Cbvs2_c= 1<<14, + Cbwp_c= 1<<13, + Cbcd2_c= 1<<12, + Cbcd1_c= 1<<11, + Cbbvd2_c= 1<<10, + Cbbvd1_c= 1<<9, + Cbrdy_l= 1<<7, + Cbrdy_h= 1<<6, + Cbrdy_r= 1<<5, + Cbrdy_f= 1<<4, + + /* pgcr[n] */ + Cbdreq_int= 0<<14, + Cbdreq_iois16= 2<<14, + Cbdreq_spkr= 3<<14, + Cboe= 1<<7, + Cbreset= 1<<6, + + /* porN */ + Rport8= 0<<6, + Rport16= 1<<6, + Rmtype= 7<<3, /* memory type field */ + Rmem= 0<<3, /* common memory space */ + Rattrib= 2<<3, /* attribute space */ + Rio= 3<<3, + Rdma= 4<<3, /* normal DMA */ + Rdmalx= 5<<3, /* DMA, last transaction */ + RA22_23= 6<<3, /* ``drive A22 and A23 signals on CE2 and CE1'' */ + RslotB= 1<<2, /* select slot B (always, on MPC823) */ + Rwp= 1<<1, /* write protect */ + Rvalid= 1<<0, /* region valid */ + + Nmap= 8, /* max number of maps to use */ + + /* + * configuration registers - they start at an offset in attribute + * memory found in the CIS. + */ + Rconfig= 0, + Creset= (1<<7), /* reset device */ + Clevel= (1<<6), /* level sensitive interrupt line */ + Rccsr= 2, + Ciack = (1<<0), + Cipend = (1<<1), + Cpwrdown= (1<<2), + Caudioen= (1<<3), + Ciois8= (1<<5), + Cchgena= (1<<6), + Cchange= (1<<7), + Rpin= 4, /* pin replacement register */ + Rscpr= 6, /* socket and copy register */ + Riob0= 10, + Riob1= 12, + Riob2= 14, + Riob3= 16, + Riolim= 18, + + Maxctab= 8, /* maximum configuration table entries */ + MaxCIS = 8192, /* maximum CIS size in bytes */ + Mgran = 8192, /* maximum size of reads and writes */ + + Statusbounce=20, /* msec debounce time */ +}; + +typedef struct Ctlr Ctlr; + +/* a controller (there's only one) */ +struct Ctlr +{ + int dev; + int nslot; + + /* memory maps */ + Lock mlock; /* lock down the maps */ + PCMmap mmap[Nmap]; /* maps */ + + /* IO port allocation */ + ulong nextport; +}; +static Ctlr controller[Maxctlr]; + +static PCMslot *slot; +static PCMslot *lastslot; +static int nslot; + +static Map pcmmapv[Nmap+1]; +static RMap pcmmaps = {"PCMCIA mappings"}; + +static void pcmciaintr(Ureg*, void*); +static void pcmciareset(void); +static int pcmio(int, ISAConf*); +static long pcmread(int, int, void*, long, ulong); +static long pcmwrite(int, int, void*, long, ulong); +static void slotdis(PCMslot*); + +static ulong pcmmalloc(ulong, long); +static void pcmfree(ulong, long); +static void pcmciaproc(void*); + +static void pcmciadump(PCMslot*); + +/* + * get info about card + */ +static void +slotinfo(PCMslot *pp) +{ + ulong pipr; + + pipr = (m->iomem->pipr >> pp->slotshift) & 0xFF00; +print("pipr=%8.8lux/%lux\n", m->iomem->pipr, pipr); + pp->v3_3 = (pipr&Cbvs1)!=0; + pp->voltage = (((pipr & Cbvs2)!=0)<<1) | ((pipr & Cbvs1)!=0); + pp->occupied = (pipr&(Cbcd1|Cbcd2))==0; + pp->powered = pcmpowered(pp->slotno); + pp->battery = (pipr & (Cbbvd1|Cbbvd2))>>9; + pp->wrprot = (pipr&Cbwp)!=0; + pp->busy = (pipr&Cbrdy)==0; +} + +static void +pcmdelay(int ms) +{ + if(up == nil) + delay(ms); + else + tsleep(&up->sleep, return0, nil, ms); +} + +/* + * enable the slot card + */ +static void +slotena(PCMslot *pp) +{ + IMM *io; + + if(pp->enabled) + return; + m->iomem->pgcr[pp->slotno] &= ~Cboe; + pcmpower(pp->slotno, 1); + eieio(); + pcmdelay(300); + io = m->iomem; + io->pgcr[pp->slotno] |= Cbreset; /* active high */ + eieio(); + pcmdelay(100); + io->pgcr[pp->slotno] &= ~Cbreset; + eieio(); + pcmdelay(500); /* ludicrous delay */ + + /* get configuration */ + slotinfo(pp); + if(pp->occupied){ + if(pp->cisread == 0){ + pcmcisread(pp); + pp->cisread = 1; + } + pp->enabled = 1; + } else{ + print("empty slot\n"); + slotdis(pp); + } +} + +/* + * disable the slot card + */ +static void +slotdis(PCMslot *pp) +{ + int i; + Ctlr *ctlr; + PCMmap *pm; + +iprint("slotdis %d\n", pp->slotno); + pcmpower(pp->slotno, 0); + m->iomem->pgcr[pp->slotno] |= Cboe; + ctlr = pp->ctlr; + for(i = 0; i < nelem(ctlr->mmap); i++){ + pm = &ctlr->mmap[i]; + if(m->iomem->pcmr[i].option & Rvalid && pm->slotno == pp->slotno) + pcmunmap(pp->slotno, pm); + } + pp->enabled = 0; + pp->cisread = 0; +} + +static void +pcmciardy(Ureg *ur, void *a) +{ + PCMslot *pp; + ulong w; + + pp = a; + w = (m->iomem->pipr >> pp->slotshift) & 0xFF00; + if(pp->occupied && (w & Cbrdy) == 0){ /* interrupt */ +print("PCM.%dI#%lux|", pp->slotno, w); + if(pp->intr.f != nil) + pp->intr.f(ur, pp->intr.arg); + } +} + +void +pcmintrenable(int slotno, void (*f)(Ureg*, void*), void *arg) +{ + PCMslot *pp; + IMM *io; + char name[KNAMELEN]; + + if(slotno < 0 || slotno >= nslot) + panic("pcmintrenable"); + snprint(name, sizeof(name), "pcmcia.irq%d", slotno); + io = ioplock(); + pp = slot+slotno; + pp->intr.f = f; + pp->intr.arg = arg; + intrenable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name); + io->per |= Cbrdy_l; /* assumes used for irq, not rdy; assumes level interrupt always right */ + iopunlock(); +} + +void +pcmintrdisable(int slotno, void (*f)(Ureg*, void*), void *arg) +{ + PCMslot *pp; + IMM *io; + char name[KNAMELEN]; + + if(slotno < 0 || slotno >= nslot) + panic("pcmintrdisable"); + snprint(name, sizeof(name), "pcmcia.irq%d", slotno); + io = ioplock(); + pp = slot+slotno; + if(pp->intr.f == f && pp->intr.arg == arg){ + pp->intr.f = nil; + pp->intr.arg = nil; + intrdisable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name); + io->per &= ~Cbrdy_l; + } + iopunlock(); +} + +/* + * status change interrupt + * + * this wakes a monitoring process to read the CIS, + * rather than holding other interrupts out here. + */ + +static Rendez pcmstate; + +static int +statechanged(void *a) +{ + PCMslot *pp; + int in; + + pp = a; + in = (m->iomem->pipr & (Cbcd1|Cbcd2))==0; + return in != pp->occupied; +} + +static void +pcmciaintr(Ureg*, void*) +{ + ulong events; + + if(slot == 0) + return; + events = m->iomem->pscr & (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c); + eieio(); + m->iomem->pscr = events; + /* TO DO: other slot */ +iprint("PCM: #%lux|", events); +iprint("pipr=#%lux|", m->iomem->pipr & 0xFF00); + wakeup(&pcmstate); +} + +static void +pcmciaproc(void*) +{ + ulong csc; + PCMslot *pp; + int was; + + for(;;){ + sleep(&pcmstate, statechanged, slot+1); + tsleep(&up->sleep, return0, nil, Statusbounce); + /* + * voltage change 1,2 + * write protect change + * card detect 1,2 + * battery voltage 1 change (or SPKR-bar) + * battery voltage 2 change (or STSCHG-bar) + * card B rdy / IRQ-bar low + * card B rdy / IRQ-bar high + * card B rdy / IRQ-bar rising edge + * card B rdy / IRQ-bar falling edge + * + * TO DO: currently only handle card-present changes + */ + + for(pp = slot; pp < lastslot; pp++){ + if(pp->memlen == 0) + continue; + csc = (m->iomem->pipr>>pp->slotshift) & (Cbcd1|Cbcd2); + was = pp->occupied; + slotinfo(pp); + if(csc == 0 && was != pp->occupied){ + if(!pp->occupied){ + slotdis(pp); + if(pp->special && pp->notify.f != nil) + pp->notify.f(pp->notify.arg, 1); + } + } + } + } +} + +static uchar greycode[] = { + 0, 1, 3, 2, 6, 7, 5, 4, 014, 015, 017, 016, 012, 013, 011, 010, + 030, 031, 033, 032, 036, 037, 035, 034, 024, 025, 027 +}; + +/* + * get a map for pc card region, return corrected len + */ +PCMmap* +pcmmap(int slotno, ulong offset, int len, int attr) +{ + Ctlr *ctlr; + PCMslot *pp; + PCMmap *pm, *nm; + IMM *io; + int i; + ulong e, bsize, code, opt; + + if(0) + print("pcmmap: %d #%lux %d #%x\n", slotno, offset, len, attr); + pp = slot + slotno; + if(!pp->occupied) + return nil; + if(attr == 1){ /* account for ../port/cis.c's conventions */ + attr = Rattrib; + if(len <= 0) + len = MaxCIS*2; /* TO DO */ + } + ctlr = pp->ctlr; + + /* convert offset to granularity */ + if(len <= 0) + len = 1; + e = offset+len; + for(i=0;; i++){ + if(i >= nelem(greycode)) + return nil; + bsize = 1<<i; + offset &= ~(bsize-1); + if(e <= offset+bsize) + break; + } + code = greycode[i]; + if(0) + print("i=%d bsize=%lud code=0%luo\n", i, bsize, code); + e = offset+bsize; + len = bsize; + + lock(&ctlr->mlock); + + /* look for an existing map that covers the right area */ + io = m->iomem; + nm = nil; + for(i=0; i<Nmap; i++){ + pm = &ctlr->mmap[i]; + if(io->pcmr[i].option & Rvalid && + pm->slotno == slotno && + pm->attr == attr && + offset >= pm->ca && e <= pm->cea){ + pm->ref++; + unlock(&ctlr->mlock); + return pm; + } + if(nm == 0 && pm->ref == 0) + nm = pm; + } + pm = nm; + if(pm == nil){ + unlock(&ctlr->mlock); + return nil; + } + + /* set up new map */ + pm->isa = pcmmalloc(offset, len); + if(pm->isa == 0){ + /* address not available: in use, or too much to map */ + unlock(&ctlr->mlock); + return 0; + } + if(0) + print("mx=%d isa=#%lux\n", (int)(pm - ctlr->mmap), pm->isa); + + pm->len = len; + pm->ca = offset; + pm->cea = pm->ca + pm->len; + pm->attr = attr; + i = pm - ctlr->mmap; + io->pcmr[i].option &= ~Rvalid; /* disable map before changing it */ + io->pcmr[i].base = pm->isa; + opt = attr; + opt |= code<<27; + if((attr&Rmtype) == Rio){ + opt |= 4<<12; /* PSST */ + opt |= 8<<7; /* PSL */ + opt |= 2<<16; /* PSHT */ + }else{ + opt |= 6<<12; /* PSST */ + opt |= 24<<7; /* PSL */ + opt |= 8<<16; /* PSHT */ + } + if((attr & Rport16) == 0) + opt |= Rport8; + if(slotno == 1) + opt |= RslotB; + io->pcmr[i].option = opt | Rvalid; + pm->slotno = slotno; + pm->ref = 1; + + unlock(&ctlr->mlock); + return pm; +} + +static void +pcmiomap(PCMslot *pp, PCMconftab *ct, int i) +{ + int n, attr; + Ctlr *ctlr; + + if(0) + print("pcm iomap #%lux %lud\n", ct->io[i].start, ct->io[i].len); + if(ct->io[i].len <= 0) + return; + if(ct->io[i].start == 0){ + n = 1<<ct->nlines; + ctlr = pp->ctlr; + lock(&ctlr->mlock); + if(ctlr->nextport == 0) + ctlr->nextport = 0xF000; + ctlr->nextport = (ctlr->nextport + n - 1) & ~(n-1); + ct->io[i].start = ctlr->nextport; + ct->io[i].len = n; + ctlr->nextport += n; + unlock(&ctlr->mlock); + } + attr = Rio; + if(ct->bit16) + attr |= Rport16; + ct->io[i].map = pcmmap(pp->slotno, ct->io[i].start, ct->io[i].len, attr); +} + +void +pcmunmap(int slotno, PCMmap* pm) +{ + int i; + PCMslot *pp; + Ctlr *ctlr; + + pp = slot + slotno; + if(pp->memlen == 0) + return; + ctlr = pp->ctlr; + lock(&ctlr->mlock); + if(pp->slotno == pm->slotno && --pm->ref == 0){ + i = pm - ctlr->mmap; + m->iomem->pcmr[i].option = 0; + m->iomem->pcmr[i].base = 0; + pcmfree(pm->isa, pm->len); + } + unlock(&ctlr->mlock); +} + +static void +increfp(PCMslot *pp) +{ + if(incref(pp) == 1) + slotena(pp); +} + +static void +decrefp(PCMslot *pp) +{ + if(decref(pp) == 0) + slotdis(pp); +} + +/* + * look for a card whose version contains 'idstr' + */ +int +pcmspecial(char *idstr, ISAConf *isa) +{ + PCMslot *pp; + extern char *strstr(char*, char*); + + pcmciareset(); + for(pp = slot; pp < lastslot; pp++){ + if(pp->special || pp->memlen == 0) + continue; /* already taken */ + increfp(pp); + if(pp->occupied && strstr(pp->verstr, idstr)){ + print("PCMslot #%d: Found %s - ",pp->slotno, idstr); + if(isa == 0 || pcmio(pp->slotno, isa) == 0){ + print("ok.\n"); + pp->special = 1; + return pp->slotno; + } + print("error with isa io\n"); + } + decrefp(pp); + } + return -1; +} + +void +pcmspecialclose(int slotno) +{ + PCMslot *pp; + + if(slotno >= nslot) + panic("pcmspecialclose"); + pp = slot + slotno; + pp->special = 0; + decrefp(pp); +} + +void +pcmnotify(int slotno, void (*f)(void*, int), void* a) +{ + PCMslot *pp; + + if(slotno < 0 || slotno >= nslot) + panic("pcmnotify"); + pp = slot + slotno; + if(pp->occupied && pp->special){ + pp->notify.f = f; + pp->notify.arg = a; + } +} + +/* + * reserve pcmcia slot address space [addr, addr+size[, + * returning a pointer to it, or nil if the space was already reserved. + */ +static ulong +pcmmalloc(ulong addr, long size) +{ + return rmapalloc(&pcmmaps, PHYSPCMCIA+addr, size, size); +} + +static void +pcmfree(ulong a, long size) +{ + if(a != 0 && size > 0) + mapfree(&pcmmaps, a, size); +} + +enum +{ + Qdir, + Qmem, + Qattr, + Qctl, +}; + +#define SLOTNO(c) ((c->qid.path>>8)&0xff) +#define TYPE(c) (c->qid.path&0xff) +#define QID(s,t) (((s)<<8)|(t)) + +static int +pcmgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) +{ + int slotno; + Qid qid; + long len; + PCMslot *pp; + + if(i>=3*nslot) + return -1; + slotno = i/3; + pp = slot + slotno; + if(pp->memlen == 0) + return 0; + len = 0; + switch(i%3){ + case 0: + qid.path = QID(slotno, Qmem); + sprint(up->genbuf, "pcm%dmem", slotno); + len = pp->memlen; + break; + case 1: + qid.path = QID(slotno, Qattr); + sprint(up->genbuf, "pcm%dattr", slotno); + len = pp->memlen; + break; + case 2: + qid.path = QID(slotno, Qctl); + sprint(up->genbuf, "pcm%dctl", slotno); + break; + } + qid.vers = 0; + devdir(c, qid, up->genbuf, len, eve, 0660, dp); + return 1; +} + +/* + * used only when debugging + */ +static void +pcmciadump(PCMslot *) +{ + IMM *io; + int i; + + io = m->iomem; + print("pipr #%4.4lux pscr #%4.4lux per #%4.4lux pgcr[1] #%8.8lux\n", + io->pipr & 0xFFFF, io->pscr & 0xFFFF, io->per & 0xFFFF, io->pgcr[1]); + for(i=0; i<8; i++) + print("pbr%d #%8.8lux por%d #%8.8lux\n", i, io->pcmr[i].base, i, io->pcmr[i].option); +} + +/* + * set up for slot cards + */ +static void +pcmciareset(void) +{ + static int already; + int i; + Ctlr *cp; + IMM *io; + PCMslot *pp; + + if(already) + return; + already = 1; + + cp = controller; + /* TO DO: set low power mode? ... */ + + mapinit(&pcmmaps, pcmmapv, sizeof(pcmmapv)); + mapfree(&pcmmaps, PHYSPCMCIA, PCMCIALEN); + + io = m->iomem; + + for(i=0; i<8; i++){ + io->pcmr[i].option = 0; + io->pcmr[i].base = 0; + } + + io->pscr = ~0; /* reset status */ + /* TO DO: Cboe, Cbreset */ + /* TO DO: two slots except on 823 */ + pcmenable(); + /* TO DO: if the card is there turn on 5V power to keep its battery alive */ + slot = xalloc(Maxslot * sizeof(PCMslot)); + lastslot = slot; + slot[0].slotshift = Slotashift; + slot[1].slotshift = 0; + for(i=0; i<Maxslot; i++){ + pp = &slot[i]; + if(!pcmslotavail(i)){ + pp->memlen = 0; + continue; + } + io->per |= (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c)<<pp->slotshift; /* enable status interrupts */ + io->pgcr[i] = (1<<(31-PCMCIAio)) | (1<<(23-PCMCIAstatus)); + pp->slotno = i; + pp->memlen = 8*MB; + pp->ctlr = cp; + //slotdis(pp); + lastslot = slot; + nslot = i+1; + } + if(1) + pcmciadump(slot); + intrenable(PCMCIAstatus, pcmciaintr, cp, BUSUNKNOWN, "pcmcia"); + print("pcmcia reset\n"); +} + +static void +pcmciainit(void) +{ + kproc("pcmcia", pcmciaproc, nil, 0); +} + +static Chan* +pcmciaattach(char *spec) +{ + return devattach('y', spec); +} + +static Walkqid* +pcmciawalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, pcmgen); +} + +static int +pcmciastat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, 0, 0, pcmgen); +} + +static Chan* +pcmciaopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else + increfp(slot + SLOTNO(c)); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +pcmciaclose(Chan *c) +{ + if(c->flag & COPEN) + if((c->qid.type & QTDIR) == 0) + decrefp(slot+SLOTNO(c)); +} + +/* a memmove using only bytes */ +static void +memmoveb(uchar *to, uchar *from, int n) +{ + while(n-- > 0) + *to++ = *from++; +} + +static long +pcmread(int slotno, int attr, void *a, long n, ulong offset) +{ + int i, len; + PCMmap *m; + void *ka; + uchar *ac; + PCMslot *pp; + + pp = slot + slotno; + if(pp->memlen < offset) + return 0; + if(pp->memlen < offset + n) + n = pp->memlen - offset; + + ac = a; + for(len = n; len > 0; len -= i){ + if((i = len) > Mgran) + i = Mgran; + m = pcmmap(pp->slotno, offset, i, attr? Rattrib: Rmem); + if(m == 0) + error("can't map PCMCIA card"); + if(waserror()){ + if(m) + pcmunmap(pp->slotno, m); + nexterror(); + } + if(offset + len > m->cea) + i = m->cea - offset; + else + i = len; + ka = (char*)KADDR(m->isa) + (offset - m->ca); + memmoveb(ac, ka, i); + poperror(); + pcmunmap(pp->slotno, m); + offset += i; + ac += i; + } + + return n; +} + +static long +pcmciaread(Chan *c, void *a, long n, vlong offset) +{ + char *cp, *buf; + ulong p; + PCMslot *pp; + + p = TYPE(c); + switch(p){ + case Qdir: + return devdirread(c, a, n, 0, 0, pcmgen); + case Qmem: + case Qattr: + return pcmread(SLOTNO(c), p==Qattr, a, n, offset); + case Qctl: + buf = malloc(READSTR); + if(buf == nil) + error(Enomem); + if(waserror()){ + free(buf); + nexterror(); + } + cp = buf; + pp = slot + SLOTNO(c); + if(pp->occupied) + cp += sprint(cp, "occupied\n"); + if(pp->enabled) + cp += sprint(cp, "enabled\n"); + if(pp->powered) + cp += sprint(cp, "powered\n"); + if(pp->configed) + cp += sprint(cp, "configed\n"); + if(pp->wrprot) + cp += sprint(cp, "write protected\n"); + if(pp->busy) + cp += sprint(cp, "busy\n"); + if(pp->v3_3) + cp += sprint(cp, "3.3v ok\n"); + cp += sprint(cp, "battery lvl %d\n", pp->battery); + cp += sprint(cp, "voltage select %d\n", pp->voltage); + /* TO DO: could return pgcr[] values for debugging */ + *cp = 0; + n = readstr(offset, a, n, buf); + poperror(); + free(buf); + break; + default: + n=0; + break; + } + return n; +} + +static long +pcmwrite(int dev, int attr, void *a, long n, ulong offset) +{ + int i, len; + PCMmap *m; + void *ka; + uchar *ac; + PCMslot *pp; + + pp = slot + dev; + if(pp->memlen < offset) + return 0; + if(pp->memlen < offset + n) + n = pp->memlen - offset; + + ac = a; + for(len = n; len > 0; len -= i){ + if((i = len) > Mgran) + i = Mgran; + m = pcmmap(pp->slotno, offset, i, attr? Rattrib: Rmem); + if(m == 0) + error("can't map PCMCIA card"); + if(waserror()){ + if(m) + pcmunmap(pp->slotno, m); + nexterror(); + } + if(offset + len > m->cea) + i = m->cea - offset; + else + i = len; + ka = (char*)KADDR(m->isa) + (offset - m->ca); + memmoveb(ka, ac, i); + poperror(); + pcmunmap(pp->slotno, m); + offset += i; + ac += i; + } + + return n; +} + +static long +pcmciawrite(Chan *c, void *a, long n, vlong offset) +{ + ulong p; + PCMslot *pp; + char buf[32]; + + p = TYPE(c); + switch(p){ + case Qctl: + if(n >= sizeof(buf)) + n = sizeof(buf) - 1; + strncpy(buf, a, n); + buf[n] = 0; + pp = slot + SLOTNO(c); + if(!pp->occupied) + error(Eio); + + /* set vpp on card */ + if(strncmp(buf, "vpp", 3) == 0){ + p = strtol(buf+3, nil, 0); + pcmsetvpp(pp->slotno, p); + } + break; + case Qmem: + case Qattr: + pp = slot + SLOTNO(c); + if(pp->occupied == 0 || pp->enabled == 0) + error(Eio); + n = pcmwrite(pp->slotno, p == Qattr, a, n, offset); + if(n < 0) + error(Eio); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev pcmciadevtab = { + 'y', + "pcmcia", + + pcmciareset, + pcmciainit, + devshutdown, + pcmciaattach, + pcmciawalk, + pcmciastat, + pcmciaopen, + devcreate, + pcmciaclose, + pcmciaread, + devbread, + pcmciawrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * configure the PCMslot for IO. We assume very heavily that we can read + * configuration info from the CIS. If not, we won't set up correctly. + */ +static int +pcmio(int slotno, ISAConf *isa) +{ + PCMslot *pp; + PCMconftab *ct, *et, *t; + PCMmap *pm; + uchar *p; + int irq, i, x; + + irq = isa->irq; + if(irq == 2) + irq = 9; + + if(slotno > nslot) + return -1; + pp = slot + slotno; + + if(!pp->occupied) + return -1; + + et = &pp->ctab[pp->nctab]; + + /* assume default is right */ + if(pp->def) + ct = pp->def; + else + ct = pp->ctab; + /* try for best match */ + if(ct->nlines == 0 || ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nlines && t->io[0].start == isa->port && ((1<<irq) & t->irqs)){ + ct = t; + break; + } + } + if(ct->nlines == 0 || ((1<<irq) & ct->irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nlines && ((1<<irq) & t->irqs)){ + ct = t; + break; + } + } + if(ct->nlines == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nlines){ + ct = t; + break; + } + } +print("slot %d: nlines=%d iolen=%lud irq=%d ct->index=%d nport=%d ct->port=#%lux/%lux\n", slotno, ct->nlines, ct->io[0].len, irq, ct->index, ct->nio, ct->io[0].start, isa->port); + if(ct == et || ct->nlines == 0) + return -1; + /* route interrupts */ + isa->irq = irq; + //wrreg(pp, Rigc, irq | Fnotreset | Fiocard); + delay(2); + + /* set power and enable device */ + pcmsetvcc(pp->slotno, ct->vcc); + pcmsetvpp(pp->slotno, ct->vpp1); + + delay(2); /* could poll BSY during power change */ + + for(i=0; i<ct->nio; i++) + pcmiomap(pp, ct, i); + + if(ct->nio) + isa->port = ct->io[0].start; + + /* only touch Rconfig if it is present */ + if(pp->cpresent & (1<<Rconfig)){ +print("Rconfig present: #%lux\n", pp->caddr+Rconfig); + /* Reset adapter */ + pm = pcmmap(slotno, pp->caddr + Rconfig, 1, Rattrib); + if(pm == nil) + return -1; + + p = (uchar*)KADDR(pm->isa) + (pp->caddr + Rconfig - pm->ca); + + /* set configuration and interrupt type */ + x = ct->index; + if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7)) + x |= Clevel; + *p = x; + delay(5); + + pcmunmap(pp->slotno, pm); +print("Adapter reset\n"); + } + + return 0; +} diff --git a/os/mpc/devrtc.c b/os/mpc/devrtc.c new file mode 100644 index 00000000..90193764 --- /dev/null +++ b/os/mpc/devrtc.c @@ -0,0 +1,258 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +/* + * MPC8xx real time clock + * optional board option switch + * optional nvram + * interrupt statistics + */ + +enum{ + Qdir, + Qrtc, + Qswitch, + Qnvram, + Qintstat, + + Qporta, + Qportb, + Qportc, + + /* sccr */ + RTDIV= 1<<24, + RTSEL= 1<<23, + + /* rtcsc */ + RTE= 1<<0, + R38K= 1<<4, +}; + +static QLock rtclock; /* mutex on clock operations */ + +static Dirtab rtcdir[]={ + ".", {Qdir,0,QTDIR}, 0, 0555, + "rtc", {Qrtc, 0}, 12, 0664, + "switch", {Qswitch, 0}, 0, 0444, + "intstat", {Qintstat, 0}, 0, 0444, + "porta", {Qporta, 0}, 0, 0444, + "portb", {Qportb, 0}, 0, 0444, + "portc", {Qportc, 0}, 0, 0444, + "nvram", {Qnvram, 0}, 0, 0660, +}; +static long nrtc = nelem(rtcdir)-1; /* excludes nvram */ + +static long readport(int, ulong, char*, long); + +static void +rtcreset(void) +{ + IMM *io; + int n; + + io = m->iomem; + io->rtcsck = KEEP_ALIVE_KEY; + n = (RTClevel<<8)|RTE; + if(m->clockgen == 5*MHz) + n |= R38K; + io->rtcsc = n; + io->rtcsck = ~KEEP_ALIVE_KEY; + if(conf.nvramsize != 0){ + rtcdir[nrtc].length = conf.nvramsize; + nrtc++; + } +} + +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, nrtc, devgen); +} + +static int +rtcstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, rtcdir, nrtc, devgen); +} + +static Chan* +rtcopen(Chan *c, int omode) +{ + return devopen(c, omode, rtcdir, nrtc, devgen); +} + +static void +rtcclose(Chan*) +{ +} + +static long +rtcread(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + ulong t; + char *b; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, nrtc, devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + t = m->iomem->rtc; + n = readnum(offset, buf, n, t, 12); + return n; + case Qswitch: + return readnum(offset, buf, n, archoptionsw(), 12); + case Qintstat: + b = malloc(2048); + if(waserror()){ + free(b); + nexterror(); + } + intrstats(b, 2048); + t = readstr(offset, buf, n, b); + poperror(); + free(b); + return t; + case Qporta: + case Qportb: + case Qportc: + return readport(c->qid.path, offset, buf, n); + case Qnvram: + if(offset < 0 || offset >= conf.nvramsize) + return 0; + if(offset + n > conf.nvramsize) + n = conf.nvramsize - offset; + memmove(buf, (char*)conf.nvrambase+offset, n); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static long +rtcwrite(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + ulong secs; + char *cp, sbuf[32]; + IMM *io; + + switch((ulong)c->qid.path){ + case Qrtc: + /* + * read the time + */ + if(offset != 0 || n >= sizeof(sbuf)-1) + error(Ebadarg); + memmove(sbuf, buf, n); + sbuf[n] = '\0'; + cp = sbuf; + while(*cp){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + /* + * set it + */ + io = ioplock(); + io->rtck = KEEP_ALIVE_KEY; + io->rtc = secs; + io->rtck = ~KEEP_ALIVE_KEY; + iopunlock(); + return n; + case Qnvram: + if(offset < 0 || offset >= conf.nvramsize) + return 0; + if(offset + n > conf.nvramsize) + n = conf.nvramsize - offset; + memmove((char*)conf.nvrambase+offset, buf, n); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static long +readport(int p, ulong offset, char *buf, long n) +{ + long t; + char *b; + int v[4], i; + IMM *io; + + io = m->iomem; + for(i=0;i<nelem(v); i++) + v[i] = 0; + switch(p){ + case Qporta: + v[0] = io->padat; + v[1] = io->padir; + v[2] = io->papar; + break; + case Qportb: + v[0] = io->pbdat; + v[1] = io->pbdir; + v[2] = io->pbpar; + break; + case Qportc: + v[0] = io->pcdat; + v[1] = io->pcdir; + v[2] = io->pcpar; + v[3] = io->pcso; + break; + } + b = malloc(READSTR); + if(waserror()){ + free(b); + nexterror(); + } + t = 0; + for(i=0; i<nelem(v); i++) + t += snprint(b+t, READSTR-t, " %8.8ux", v[i]); + t = readstr(offset, buf, n, b); + poperror(); + free(b); + return t; +} + +long +rtctime(void) +{ + return m->iomem->rtc; +} + +Dev rtcdevtab = { + 'r', + "rtc", + + rtcreset, + devinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + devcreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/mpc/devtouch.c b/os/mpc/devtouch.c new file mode 100644 index 00000000..b11aecc8 --- /dev/null +++ b/os/mpc/devtouch.c @@ -0,0 +1,497 @@ +#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)print +#define THREEBUT 0 /* !=0, enable 3-button emulation (see below) */ + +/* + * DynaPro touch panel and Maxim MAX192 A/D converter on + * York Electronics Centre's BRD/98/024 (Version A) interface, + * accessed via mpc8xx SPI interface (see spi.c). + * + * The highest level of the driver is derived from the ARM/UCB touch panel driver, + * simplified because of the differences between the panels. + */ + +/* + * Values determined by interface board + */ +enum { + /* MAX192 control words */ + MeasureX = (1<<7)|(0<<6)|(1<<3)|(1<<2)|3, /* start, channel 0, unipolar, single-ended, external clock */ + MeasureY = (1<<7)|(1<<6)|(1<<3)|(1<<2)|3, /* start, channel 1, unipolar, single-ended, external clock */ + + /* port B bits */ + ADselect = IBIT(16), /* chip select to MAX192, active low */ + + /* port C bits */ + Xenable = 1<<2, /* PC13: TOUCH_XEN, active low */ + Yenable = 1<<3, /* PC12: TOUCH_YEN, active low */ + Touched = 1<<10, /* PC5: contact detect, active low */ + + /* interrupt control via port C */ + TouchIRQ= 2, /* parallel i/o - PC5 */ + TouchEnable= 1<<TouchIRQ, /* mask for cimr */ + + /* other parameters */ + Nconverge = 10, /* maximum iterations for convergence */ + MaxDelta = 2, /* acceptable change in X/Y between iterations */ +}; + +/* + * ADC interface via SPI (see MAX192 data sheet) + * select the ADC + * send 8-bit control word and two zero bytes to clock the conversion + * receive three data bytes + * deselect the ADC and return the result + */ +static int +getcoord(int cw) +{ + uchar tbuf[3], rbuf[3]; + IMM *io; + int nr; + + tbuf[0] = cw; + tbuf[1] = 0; + tbuf[2] = 0; + io = ioplock(); + io->pbdat &= ~ADselect; + iopunlock(); + nr = spioutin(tbuf, sizeof(tbuf), rbuf); + io = ioplock(); + io->pbdat |= ADselect; + iopunlock(); + if(nr != 3) + return -1; + return ((rbuf[1]<<8)|rbuf[2])>>5; +} + +/* + * keep reading the a/d until the value stabilises + */ +static int +dejitter(int enable, int cw) +{ + int i, diff, prev, v; + IMM *io; + + io = ioplock(); + io->pcdat &= ~enable; /* active low */ + iopunlock(); + + i = 0; + v = getcoord(cw); + do{ + prev = v; + v = getcoord(cw); + diff = v - prev; + if(diff < 0) + diff = -diff; + }while(diff >= MaxDelta && ++i <= Nconverge); + + io = ioplock(); + io->pcdat |= enable; + iopunlock(); + return v; +} + +static void +adcreset(void) +{ + IMM *io; + + /* select port pins */ + io = ioplock(); + io->pcdir &= ~(Xenable|Yenable); /* ensure set to input before changing state */ + io->pcpar &= ~(Xenable|Yenable); + io->pcdat |= Xenable|Yenable; + io->pcdir |= Xenable; /* change enable bits to output one at a time to avoid both being low at once (could damage panel) */ + io->pcdat |= Xenable; /* ensure it's high after making it an output */ + io->pcdir |= Yenable; + io->pcdat |= Yenable; /* ensure it's high after making it an output */ + io->pcso &= ~(Xenable|Yenable); + io->pbdat |= ADselect; + io->pbpar &= ~ADselect; + io->pbdir |= ADselect; + iopunlock(); +} + +/* + * high-level touch panel interface + */ + +/* to and from fixed point */ +#define FX(n) ((n)<<16) +#define XF(v) ((v)>>16) + +typedef struct Touch Touch; + +struct Touch { + Lock; + Rendez r; + int m[2][3]; /* transformation matrix */ + int rate; + int down; + int raw_count; + int valid_count; + int wake_time; + int sleep_time; +}; + +static Touch touch = { + {0}, + .r {0}, + .m {{FX(1), 0, 0},{0, FX(1), 0}}, /* default is 1:1 */ + .rate 20, /* milliseconds */ +}; + +/* + * panel-touched state and interrupt + */ + +static int +touching(void) +{ + eieio(); + return (m->iomem->pcdat & Touched) == 0; +} + +static int +ispendown(void*) +{ + return touch.down || touching(); +} + +static void +touchintr(Ureg*, void*) +{ + if((m->iomem->pcdat & Touched) == 0){ + m->iomem->cimr &= ~TouchEnable; /* mask interrupts when reading pen */ + touch.down = 1; + wakeup(&touch.r); + } +} + +static void +touchenable(void) +{ + IMM *io; + + io = ioplock(); + io->cimr |= TouchEnable; + iopunlock(); +} + +/* + * touchctl commands: + * X a b c - set X transformation + * Y d e f - set Y transformation + * s<delay> - set sample delay in millisec per sample + * r<delay> - set read delay in microsec + * R<l2nr> - set log2 of number of readings to average + */ + +enum{ + Qdir, + Qtouchctl, + Qtouchstat, + Qtouch, +}; + +Dirtab touchdir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "touchctl", {Qtouchctl, 0}, 0, 0666, + "touchstat", {Qtouchstat, 0}, 0, 0444, + "touch", {Qtouch, 0}, 0, 0444, +}; + +static int +ptmap(int *m, int x, int y) +{ + return XF(m[0]*x + m[1]*y + m[2]); +} + +/* + * read a point from the touch panel; + * returns true iff the point is valid, otherwise x, y aren't changed + */ +static int +touchreadxy(int *fx, int *fy) +{ + int rx, ry; + + if(touching()){ + rx = dejitter(Xenable, MeasureX); + ry = dejitter(Yenable, MeasureY); + microdelay(40); + if(rx >=0 && ry >= 0){ + if(0) + print("touch %d %d\n", rx, ry); + *fx = ptmap(touch.m[0], rx, ry); + *fy = ptmap(touch.m[1], rx, ry); + touch.raw_count++; + return 1; + } + } + return 0; +} + +#define timer_start() 0 /* could use TBL if necessary */ +#define tmr2us(n) 0 + +static void +touchproc(void*) +{ + int b, i, x, y; + ulong t1, t2; + + t1 = timer_start(); + b = 1; + for(;;) { + //setpri(PriHi); + do{ + touch.down = 0; + touch.wake_time += (t2 = timer_start())-t1; + touchenable(); + sleep(&touch.r, ispendown, nil); + touch.sleep_time += (t1 = timer_start())-t2; + }while(!touchreadxy(&x, &y)); + + /* 640x480-specific 3-button emulation hack: */ + if(THREEBUT){ + if(y > 481) { + b = ((639-x) >> 7); + continue; + } else if(y < -2) { + b = (x >> 7)+3; + continue; + } + } + + DPRINT("#%d %d", x, y); + mousetrack(b, x, y, 0); + setpri(PriNormal); + while(touching()) { + for(i=0; i<3; i++) + if(touchreadxy(&x, &y)) { + DPRINT("*%d %d", x, y); + mousetrack(b, x, y, 0); + break; + } + touch.wake_time += (t2 = timer_start())-t1; + tsleep(&touch.r, return0, nil, touch.rate); + touch.sleep_time += (t1 = timer_start())-t2; + } + mousetrack(0, x, y, 0); + b = 1; /* go back to just button one for next press */ + } +} + +static void +touchreset(void) +{ + IMM *io; + + spireset(); + adcreset(); + intrenable(VectorCPIC+TouchIRQ, touchintr, &touch, BUSUNKNOWN, "touch"); + + /* set i/o pin to interrupt when panel touched */ + io = ioplock(); + io->pcdat &= ~Touched; + io->pcpar &= ~Touched; + io->pcdir &= ~Touched; + io->pcso &= ~Touched; + io->pcint |= Touched; /* high-to-low trigger */ + io->cimr &= ~TouchEnable; /* touchproc will enable when ready */ + iopunlock(); +} + +static void +touchinit(void) +{ + static int done; + + if(!done){ + done = 1; + kproc( "touchscreen", touchproc, nil, 0); + } +} + +static Chan* +touchattach(char* spec) +{ + return devattach('T', spec); +} + +static Walkqid* +touchwalk(Chan* c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, touchdir, nelem(touchdir), devgen); +} + +static int +touchstat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, touchdir, nelem(touchdir), devgen); +} + +static Chan* +touchopen(Chan* c, int omode) +{ + omode = openmode(omode); + switch((ulong)c->qid.path){ + case Qtouchctl: + case Qtouchstat: + if(!iseve()) + error(Eperm); + break; + } + return devopen(c, omode, touchdir, nelem(touchdir), devgen); +} + +static void +touchclose(Chan*) +{ +} + +static long +touchread(Chan* c, void* buf, long n, vlong offset) +{ + char *tmp; + int x, y; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, touchdir, nelem(touchdir), devgen); + + tmp = malloc(READSTR); + if(waserror()){ + free(tmp); + nexterror(); + } + switch((ulong)c->qid.path){ + case Qtouch: + if(!touchreadxy(&x, &y)) + x = y = -1; + snprint(tmp, READSTR, "%d %d", x, y); + break; + case Qtouchctl: + snprint(tmp, READSTR, "s%d\nr%d\nR%d\nX %d %d %d\nY %d %d %d\n", + touch.rate, 0, 1, + touch.m[0][0], touch.m[0][1], touch.m[0][2], + touch.m[1][0], touch.m[1][1], touch.m[1][2]); + break; + case Qtouchstat: + snprint(tmp, READSTR, "%d %d\n%d %d\n", + touch.raw_count, touch.valid_count, tmr2us(touch.sleep_time), tmr2us(touch.wake_time)); + touch.raw_count = 0; + touch.valid_count = 0; + touch.sleep_time = 0; + touch.wake_time = 0; + break; + default: + error(Ebadarg); + return 0; + } + n = readstr(offset, buf, n, tmp); + poperror(); + free(tmp); + return n; +} + +static void +dotouchwrite(Chan *c, char *buf) +{ + char *field[8]; + int nf, cmd, pn, m[3], n; + + nf = getfields(buf, field, nelem(field), 1, " \t\n"); + if(nf <= 0) + return; + switch((ulong)c->qid.path){ + case Qtouchctl: + cmd = *(field[0])++; + pn = *field[0] == 0; + switch(cmd) { + case 's': + n = strtol(field[pn], 0, 0); + if(n <= 0) + error(Ebadarg); + touch.rate = n; + break; + case 'r': + /* touch read delay */ + break; + case 'X': + case 'Y': + if(nf < pn+2) + error(Ebadarg); + m[0] = strtol(field[pn], 0, 0); + m[1] = strtol(field[pn+1], 0, 0); + m[2] = strtol(field[pn+2], 0, 0); + memmove(touch.m[cmd=='Y'], m, sizeof(touch.m[0])); + break; + case 'c': + case 'C': + case 'v': + case 't': + case 'e': + /* not used */ + /* break; */ + default: + error(Ebadarg); + } + break; + default: + error(Ebadarg); + return; + } +} + +static long +touchwrite(Chan* c, void* vp, long n, vlong) +{ + char buf[64]; + char *cp, *a; + int n0 = n; + int bn; + + a = vp; + while(n) { + bn = (cp = memchr(a, '\n', n))!=nil ? cp-a+1 : n; + n -= bn; + bn = bn > sizeof(buf)-1 ? sizeof(buf)-1 : bn; + memmove(buf, a, bn); + buf[bn] = '\0'; + a = cp; + dotouchwrite(c, buf); + } + return n0-n; +} + +Dev touchdevtab = { + 'T', + "touch", + + touchreset, + touchinit, + devshutdown, + touchattach, + touchwalk, + touchstat, + touchopen, + devcreate, + touchclose, + touchread, + devbread, + touchwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/mpc/devuart.c b/os/mpc/devuart.c new file mode 100644 index 00000000..13adf351 --- /dev/null +++ b/os/mpc/devuart.c @@ -0,0 +1,1450 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/netif.h" + +enum { + Nbuf= 2, /* double buffered */ + Rbufsize= 512, + Bufsize= (Rbufsize+CACHELINESZ-1)&~(CACHELINESZ-1), + Nuart= 2+4, /* max in any 8xx architecture (2xSMC, 4xSCC) */ + CTLS= 's'&037, + CTLQ= 'q'&037, +}; + +enum { + /* status bits in SCC receive buffer descriptors */ + RxBRK= 1<<7, /* break ended frame (async hdlc) */ + RxDE= 1<<7, /* DPLL error (hdlc) */ + RxBOF= 1<<6, /* BOF ended frame (async hdlc) */ + RxLG= 1<<5, /* frame too large (hdlc) */ + RxNO= 1<<4, /* bad bit alignment (hdlc) */ + RxBR= 1<<5, /* break received during frame (uart) */ + RxFR= 1<<4, /* framing error (uart) */ + RxPR= 1<<3, /* parity error (uart) */ + RxAB= 1<<3, /* frame aborted (hdlc, async hdlc) */ + RxCR= 1<<2, /* bad CRC (hdlc, async hdlc) */ + RxOV= 1<<1, /* receiver overrun (all) */ + RxCD= 1<<0, /* CD lost (all) */ + + /* hdlc-specific Rx/Tx BDs */ + TxTC= 1<<10, +}; + +/* + * SMC in UART mode + */ + +typedef struct Uartsmc Uartsmc; +struct Uartsmc { + IOCparam; + ushort maxidl; + ushort idlc; + ushort brkln; + ushort brkec; + ushort brkcr; + ushort rmask; +}; + +/* + * SCC2 UART parameters + */ +enum { + /* special mode bits */ + SccAHDLC = 1<<0, + SccHDLC = 1<<1, + SccIR = 1<<2, + SccPPP = 1<<3, +}; + +typedef struct Uartscc Uartscc; +struct Uartscc { + SCCparam; + uchar rsvd[8]; + ushort max_idl; + ushort idlc; + ushort brkcr; + ushort parec; + ushort frmec; + ushort nosec; + ushort brkec; + ushort brkln; + ushort uaddr1; + ushort uaddr2; + ushort rtemp; + ushort toseq; + ushort character[8]; + ushort rccm; + ushort rccrp; + ushort rlbc; +}; + +typedef struct UartAHDLC UartAHDLC; +struct UartAHDLC { + SCCparam; + ulong rsvd1; + ulong c_mask; + ulong c_pres; + ushort bof; + ushort eof; + ushort esc; + ushort rsvd2[2]; + ushort zero; + ushort rsvd3; + ushort rfthr; + ushort resvd4[2]; + ulong txctl_tbl; + ulong rxctl_tbl; + ushort nof; + ushort rsvd5; +}; + +typedef struct UartHDLC UartHDLC; +struct UartHDLC { + SCCparam; + ulong rsvd1; + ulong c_mask; + ulong c_pres; + ushort disfc; + ushort crcec; + ushort abtsc; + ushort nmarc; + ushort retrc; + ushort mflr; + ushort max_cnt; + ushort rfthr; + ushort rfcnt; + ushort hmask; + ushort haddr[4]; + ushort tmp; + ushort tmp_mb; +}; + +enum { + /* SCC events of possible interest here eventually */ + AB= 1<<9, /* autobaud detected */ + GRA= 1<<7, /* graceful stop completed */ + CCR= 1<<3, /* control character detected */ + + /* SCC, SMC common interrupt events */ + BSY= 1<<2, /* receive buffer was busy (overrun) */ + TXB= 1<<1, /* block sent */ + RXB= 1<<0, /* block received */ + + /* SCC events */ + TXE = 1<<4, /* transmission error */ + RXF = 1<<3, /* final block received */ + + /* gsmr_l */ + ENR = 1<<5, /* enable receiver */ + ENT = 1<<4, /* enable transmitter */ + + /* port A */ + RXD1= SIBIT(15), + TXD1= SIBIT(14), + + /* port B */ + RTS1B= IBIT(19), + + /* port C */ + RTS1C= SIBIT(15), + CTS1= SIBIT(11), + CD1= SIBIT(10), +}; + +typedef struct Uart Uart; +struct Uart +{ + QLock; + + Uart *elist; /* next enabled interface */ + char name[KNAMELEN]; + + int x; /* index: x in SMCx or SCCx */ + int cpmid; /* eg, SCC1ID, SMC1ID */ + CPMdev* cpm; + int opens; + uchar bpc; /* bits/char */ + uchar parity; + uchar stopb; + uchar setup; + uchar enabled; + int dev; + + ulong frame; /* framing errors */ + ulong perror; + ulong overrun; /* rcvr overruns */ + ulong crcerr; + ulong interrupts; + int baud; /* baud rate */ + + /* flow control */ + int xonoff; /* software flow control on */ + int blocked; + int modem; /* hardware flow control on */ + int cts; /* ... cts state */ + int rts; /* ... rts state */ + Rendez r; + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + /* staging areas to avoid some of the per character costs */ + /* TO DO: should probably use usual Ring */ + Block* istage[Nbuf]; /* double buffered */ + int rdrx; /* last buffer read */ + + Lock plock; /* for output variables */ + Block* outb; /* currently transmitting Block */ + + BD* rxb; + BD* txb; + + SMC* smc; + SCC* scc; + IOCparam* param; + ushort* brkcr; /* brkcr location in appropriate block */ + int brgc; + int mode; + Block* partial; + int loopback; +}; + +static Uart *uart[Nuart]; +static int nuart; + +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +static void uartintr(Uart*, int); +static void smcuintr(Ureg*, void*); +static void sccuintr(Ureg*, void*); + +static void +uartsetbuf(Uart *up) +{ + IOCparam *p; + BD *bd; + int i; + Block *bp; + + p = up->param; + p->rfcr = 0x18; + p->tfcr = 0x18; + p->mrblr = Rbufsize; + + if((bd = up->rxb) == nil){ + bd = bdalloc(Nbuf); + up->rxb = bd; + } + p->rbase = (ushort)bd; + for(i=0; i<Nbuf; i++){ + bd->status = BDEmpty|BDInt; + bd->length = 0; + if((bp = up->istage[i]) == nil) + up->istage[i] = bp = allocb(Bufsize); + bd->addr = PADDR(bp->wp); + dcflush(bp->wp, Bufsize); + bd++; + } + (bd-1)->status |= BDWrap; + up->rdrx = 0; + + if((bd = up->txb) == nil){ + bd = bdalloc(1); + up->txb = bd; + } + p->tbase = (ushort)bd; + bd->status = BDWrap|BDInt; + bd->length = 0; + bd->addr = 0; +} + +static void +smcsetup(Uart *up) +{ + IMM *io; + Uartsmc *p; + SMC *smc; + ulong txrx; + + archdisableuart(up->cpmid); + up->brgc = brgalloc(); + if(up->brgc < 0) + error(Eio); + m->iomem->brgc[up->brgc] = baudgen(up->baud, 16) | BaudEnable; + smcnmsi(up->x, up->brgc); + + archenableuart(up->cpmid, 0); + + if(up->x == 1) + txrx = IBIT(24)|IBIT(25); /* SMC1 RX/TX */ + else + txrx = IBIT(20)|IBIT(21); /* SMC2 */ + io = ioplock(); + io->pbpar |= txrx; + io->pbdir &= ~txrx; + iopunlock(); + + up->param = up->cpm->param; + uartsetbuf(up); + + cpmop(up->cpm, InitRxTx, 0); + + /* SMC protocol parameters */ + p = (Uartsmc*)up->param; + up->brkcr = &p->brkcr; + p->maxidl = 1; /* non-zero so buffer closes when idle before mrblr reached */ + p->brkln = 0; + p->brkec = 0; + p->brkcr = 1; + smc = up->cpm->regs; + smc->smce = 0xff; /* clear events */ + smc->smcm = BSY|RXB|TXB; /* enable all possible interrupts */ + up->smc = smc; + smc->smcmr = ((1+8+1-1)<<11)|(2<<4); /* 8-bit, 1 stop, no parity; UART mode */ + intrenable(VectorCPIC+up->cpm->irq, smcuintr, up, BUSUNKNOWN, up->name); + /* enable when device opened */ +} + +static void +smcuintr(Ureg*, void *a) +{ + Uart *up; + int events; + + up = a; + events = up->smc->smce; + eieio(); + up->smc->smce = events; + uartintr(up, events&(BSY|RXB|TXB)); +} + +/* + * set the IO ports to enable the control signals for SCCx + */ +static void +sccuartpins(int x, int mode) +{ + IMM *io; + int i, w; + + x--; + io = ioplock(); + i = 2*x; + w = (TXD1|RXD1)<<i; /* TXDn and RXDn in port A */ + io->papar |= w; /* enable TXDn and RXDn pins */ + io->padir &= ~w; + if((mode & SccIR) == 0) + io->paodr |= TXD1<<i; + else + io->paodr &= ~w; /* not open drain */ + + w = (CD1|CTS1)<<i; /* CDn and CTSn in port C */ + io->pcpar &= ~w; + io->pcdir &= ~w; + if(conf.nocts2 || mode) + io->pcso &= ~w; /* force CTS and CD on */ + else + io->pcso |= w; + + w = RTS1B<<x; + io->pbpar &= ~w; + io->pbdir &= ~w; + + w = RTS1C<<x; /* RTSn~ */ + if((mode & SccIR) == 0) + io->pcpar |= w; + else + io->pcpar &= ~w; /* don't use for IR */ + iopunlock(); +} + +static void +sccsetup(Uart *up) +{ + SCC *scc; + int i; + + scc = up->cpm->regs; + up->scc = scc; + up->param = up->cpm->param; + sccxstop(up->cpm); + archdisableuart(up->cpmid); + if(up->brgc < 0){ + up->brgc = brgalloc(); + if(up->brgc < 0) + error(Eio); + } + m->iomem->brgc[up->brgc] = baudgen(up->baud, 16) | BaudEnable; + sccnmsi(up->x, up->brgc, up->brgc); + sccuartpins(up->x, up->mode); + + uartsetbuf(up); + + cpmop(up->cpm, InitRxTx, 0); + + /* SCC protocol parameters */ + if((up->mode & (SccAHDLC|SccHDLC)) == 0){ + Uartscc *sp; + sp = (Uartscc*)up->param; + sp->max_idl = 1; + sp->brkcr = 1; + sp->parec = 0; + sp->frmec = 0; + sp->nosec = 0; + sp->brkec = 0; + sp->brkln = 0; + sp->brkec = 0; + sp->uaddr1 = 0; + sp->uaddr2 = 0; + sp->toseq = 0; + for(i=0; i<8; i++) + sp->character[i] = 0x8000; + sp->rccm = 0xC0FF; + up->brkcr = &sp->brkcr; + scc->irmode = 0; + scc->dsr = ~0; + scc->gsmrh = 1<<5; /* 8-bit oriented receive fifo */ + scc->gsmrl = 0x28004; /* UART mode */ + }else{ + UartAHDLC *hp; + hp = (UartAHDLC*)up->param; + hp->c_mask = 0x0000F0B8; + hp->c_pres = 0x0000FFFF; + if(up->mode & SccIR){ + hp->bof = 0xC0; + hp->eof = 0xC1; + //scc->dsr = 0xC0C0; + scc->dsr = 0x7E7E; + }else{ + hp->bof = 0x7E; + hp->eof = 0x7E; + scc->dsr = 0x7E7E; + } + hp->esc = 0x7D; + hp->zero = 0; + if(up->mode & SccHDLC) + hp->rfthr = 1; + else + hp->rfthr = 0; /* receive threshold of 1 doesn't work properly for Async HDLC */ + hp->txctl_tbl = 0; + hp->rxctl_tbl = 0; + if(up->mode & SccIR){ + /* low-speed infrared */ + hp->nof = 12-1; /* 12 flags */ + scc->irsip = 0; + scc->irmode = (2<<8) | 1; + archsetirxcvr(0); + if(up->loopback) + scc->irmode = (3<<4)|1; /* loopback */ + }else{ + scc->irmode = 0; + hp->txctl_tbl = ~0; + hp->rxctl_tbl = ~0; + hp->nof = 1-1; /* one opening flag */ + } + up->brkcr = nil; + scc->gsmrh = 1<<5; /* 8-bit oriented receive fifo */ + if(up->mode & SccHDLC) + scc->gsmrl = 0x28000; /* HDLC */ + else + scc->gsmrl = 0x28006; /* async HDLC/IrDA */ + } + archenableuart(up->cpmid, (up->mode&SccIR)!=0); + scc->scce = ~0; /* clear events */ + scc->sccm = TXE|BSY|RXF|TXB|RXB; /* enable all interesting interrupts */ + intrenable(VectorCPIC+up->cpm->irq, sccuintr, up, BUSUNKNOWN, up->name); + scc->psmr = 3<<12; /* 8-bit, 1 stop, no parity; UART mode */ + if(up->loopback && (up->mode & SccIR) == 0) + scc->gsmrl |= 1<<6; /* internal loop back */ + scc->gsmrl |= ENT|ENR; /* enable rx/tx */ + if(0){ + print("gsmrl=%8.8lux gsmrh=%8.8lux dsr=%4.4ux irmode=%4.4ux\n", scc->gsmrl, scc->gsmrh, scc->dsr, scc->irmode); + for(i=0; i<sizeof(Uartscc); i+=4) + print("%2.2ux %8.8lux\n", i, *(ulong*)((uchar*)up->param+i)); + } +} + +static void +sccuintr(Ureg*, void *a) +{ + Uart *up; + int events; + + up = a; + if(up->scc == nil) + return; + events = up->scc->scce; + eieio(); + up->scc->scce = events; + if(up->enabled){ + if(0) + print("#%ux|", events); + uartintr(up, events); + } +} + +static void +uartsetbaud(Uart *p, int rate) +{ + if(rate <= 0 || p->brgc < 0) + return; + p->baud = rate; + m->iomem->brgc[p->brgc] = baudgen(rate, 16) | BaudEnable; +} + +static void +uartsetmode(Uart *p) +{ + int r, clen; + + ilock(&p->plock); + clen = p->bpc; + if(p->parity == 'e' || p->parity == 'o') + clen++; + clen++; /* stop bit */ + if(p->stopb == 2) + clen++; + if(p->smc){ + r = p->smc->smcmr & 0x3F; /* keep mode, enable bits */ + r |= (clen<<11); + if(p->parity == 'e') + r |= 3<<8; + else if(p->parity == 'o') + r |= 2<<8; + if(p->stopb == 2) + r |= 1<<10; + eieio(); + p->smc->smcmr = r; + }else if(p->scc && p->mode == 0){ + r = p->scc->psmr & 0x8FE0; /* keep mode bits */ + r |= ((p->bpc-5)&3)<<12; + if(p->parity == 'e') + r |= (6<<2)|2; + else if(p->parity == 'o') + r |= (4<<2)|0; + if(p->stopb == 2) + r |= 1<<14; + eieio(); + p->scc->psmr = r; + } + iunlock(&p->plock); +} + +static void +uartparity(Uart *p, char type) +{ + ilock(&p->plock); + p->parity = type; + iunlock(&p->plock); + uartsetmode(p); +} + +/* + * set bits/character + */ +static void +uartbits(Uart *p, int bits) +{ + if(bits < 5 || bits > 14 || bits > 8 && p->scc) + error(Ebadarg); + + ilock(&p->plock); + p->bpc = bits; + iunlock(&p->plock); + uartsetmode(p); +} + + +/* + * toggle DTR + */ +static void +uartdtr(Uart *p, int n) +{ + if(p->scc == nil) + return; /* not possible */ + USED(n); /* not possible on FADS */ +} + +/* + * toggle RTS + */ +static void +uartrts(Uart *p, int n) +{ + p->rts = n; + if(p->scc == nil) + return; /* not possible */ + USED(n); /* not possible on FADS */ +} + +/* + * send break + */ +static void +uartbreak(Uart *p, int ms) +{ + if(p->brkcr == nil) + return; + + if(ms <= 0) + ms = 200; + + if(waserror()){ + ilock(&p->plock); + *p->brkcr = 1; + cpmop(p->cpm, RestartTx, 0); + iunlock(&p->plock); + nexterror(); + } + ilock(&p->plock); + *p->brkcr = ((p->baud/(p->bpc+2))*ms+500)/1000; + cpmop(p->cpm, StopTx, 0); + iunlock(&p->plock); + + tsleep(&up->sleep, return0, 0, ms); + + poperror(); + ilock(&p->plock); + *p->brkcr = 1; + cpmop(p->cpm, RestartTx, 0); + iunlock(&p->plock); +} + +/* + * modem flow control on/off (rts/cts) + */ +static void +uartmflow(Uart *p, int n) +{ + if(p->scc == nil) + return; /* not possible */ + if(n){ + p->modem = 1; + /* enable status interrupts ... */ + p->scc->psmr |= 1<<15; /* enable async flow control */ + p->cts = 1; + /* could change maxidl */ + }else{ + p->modem = 0; + /* stop status interrupts ... */ + p->scc->psmr &= ~(1<<15); + p->cts = 1; + } +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +void +uartenable(Uart *p) +{ + Uart **l; + + if(p->enabled) + return; + + if(p->setup == 0){ + if(p->cpmid == CPsmc1 || p->cpmid == CPsmc2) + smcsetup(p); + else + sccsetup(p); + p->setup = 1; + } + + /* + * turn on interrupts + */ + if(p->smc){ + cpmop(p->cpm, RestartTx, 0); + p->smc->smcmr |= 3; + p->smc->smcm = BSY|TXB|RXB; + eieio(); + }else if(p->scc){ + cpmop(p->cpm, RestartTx, 0); + p->scc->gsmrl |= ENT|ENR; + p->scc->sccm = BSY|TXB|RXB; + eieio(); + } + + /* + * turn on DTR and RTS + */ + uartdtr(p, 1); + uartrts(p, 1); + + /* + * assume we can send + */ + p->cts = 1; + p->blocked = 0; + + /* + * set baud rate to the last used + */ + uartsetbaud(p, p->baud); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + unlock(&uartalloc); + p->cts = 1; + p->blocked = 0; + p->xonoff = 0; + p->enabled = 1; +} + +/* + * turn off a port's interrupts. reset DTR and RTS + */ +void +uartdisable(Uart *p) +{ + Uart **l; + + /* + * turn off interrpts + */ + if(p->smc) + smcxstop(p->cpm); + else if(p->scc) + sccxstop(p->cpm); + + /* + * revert to default settings + */ + p->bpc = 8; + p->parity = 0; + p->stopb = 0; + + /* + * turn off DTR, RTS, hardware flow control & fifo's + */ + uartdtr(p, 0); + uartrts(p, 0); + uartmflow(p, 0); + p->xonoff = p->blocked = 0; + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + unlock(&uartalloc); +} + +/* + * set the next output buffer going + */ +static void +txstart(Uart *p) +{ + Block *b; + int n, flags; + + if(!p->cts || p->blocked || p->txb->status & BDReady) + return; + if((b = p->outb) == nil){ + if((b = qget(p->oq)) == nil) + return; + if(p->mode & SccPPP && + p->mode & SccAHDLC && + BLEN(b) >= 8){ /* strip framing data */ + UartAHDLC *hp; + hp = (UartAHDLC*)p->param; + if(hp != nil && (p->mode & SccIR) == 0){ + hp->txctl_tbl = nhgetl(b->rp); + hp->rxctl_tbl = nhgetl(b->rp+4); + } + b->rp += 8; + if(0) + print("tx #%lux rx #%lux\n", hp->txctl_tbl, hp->rxctl_tbl); + } + } + n = BLEN(b); + if(n <= 0) + print("txstart: 0\n"); + if(p->bpc > 8){ + /* half-word alignment and length if chars are long */ + if(PADDR(b->rp)&1){ /* must be even if chars are long */ + memmove(b->base, b->rp, n); + b->rp = b->base; + b->wp = b->rp+n; + } + if(n & 1) + n++; + } + dcflush(b->rp, n); + p->outb = b; + if(n > 0xFFFF) + n = 0xFFFE; + if(p->mode & SccHDLC) + flags = BDLast | TxTC; + else if(p->mode) + flags = BDLast; + else + flags = 0; + p->txb->addr = PADDR(b->rp); + p->txb->length = n; + eieio(); + p->txb->status = (p->txb->status & BDWrap) | flags | BDReady|BDInt; + eieio(); +} + +/* + * (re)start output + */ +static void +uartkick(void *v) +{ + Uart *p; + + p = v; + ilock(&p->plock); + if(p->outb == nil) + txstart(p); + iunlock(&p->plock); +} + +/* + * restart input if it's off + */ +static void +uartflow(void *v) +{ + Uart *p; + + p = v; + if(p->modem) + uartrts(p, 1); +} + +static void +uartsetup(int x, int lid, char *name) +{ + Uart *p; + + if(nuart >= Nuart) + return; + + p = xalloc(sizeof(Uart)); + uart[nuart] = p; + strcpy(p->name, name); + p->dev = nuart; + nuart++; + p->x = x; + p->cpmid = lid; + p->cpm = cpmdev(lid); + p->brgc = -1; + p->mode = 0; + + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + p->bpc = 8; + p->parity = 0; + p->baud = 9600; + + p->iq = qopen(4*1024, Qcoalesce, uartflow, p); + p->oq = qopen(4*1024, 0, uartkick, p); +} + +/* + * called by main() to configure a duart port as a console or a mouse + */ +void +uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int)) +{ + Uart *p; + + if(port < 0 || port >= nuart || (p = uart[port]) == nil) + return; /* specified port not implemented */ + uartenable(p); + if(baud) + uartsetbaud(p, baud); + p->putc = putc; + if(in) + *in = p->iq; + if(out) + *out = p->oq; + p->opens++; +} + +static int +uartinput(Uart *p, BD *bd) +{ + int ch, dokick, i, l; + uchar *bp; + + dokick = 0; + if(bd->status & RxFR) + p->frame++; + if(bd->status & RxOV) + p->overrun++; + l = bd->length; + if(bd->status & RxPR){ + p->perror++; + l--; /* it's the last character */ + } + bp = KADDR(bd->addr); + if(p->xonoff || p->putc && p->opens==1){ + for(i=0; i<l; i++){ + ch = bp[i]; + if(p->xonoff){ + if(ch == CTLS){ + p->blocked = 1; + cpmop(p->cpm, StopTx, 0); + }else if (ch == CTLQ){ + p->blocked = 0; + dokick = 1; + } + /* BUG? should discard on/off char? */ + } + if(p->putc) + (*p->putc)(p->iq, ch); + } + } + if(l > 0 && (p->putc == nil || p->opens>1)) + qproduce(p->iq, bp, l); + return dokick; +} + +static void +framedinput(Uart *p, BD *bd) +{ + Block *pkt; + int l; + + pkt = p->partial; + p->partial = nil; + if(bd->status & RxOV){ + p->overrun++; + goto Discard; + } + if(bd->status & (RxAB|RxCR|RxCD|RxLG|RxNO|RxDE|RxBOF|RxBRK)){ + if(bd->status & RxCR) + p->crcerr++; + else + p->frame++; + goto Discard; + } + if(pkt == nil){ + pkt = iallocb(1500); /* TO DO: allocate less if possible */ + if(pkt == nil) + return; + } + l = bd->length; + if(bd->status & BDLast) + l -= BLEN(pkt); /* last one gives size of entire frame */ + if(l > 0){ + if(pkt->wp+l > pkt->lim) + goto Discard; + memmove(pkt->wp, KADDR(bd->addr), l); + pkt->wp += l; + } + if(0) + print("#%ux|", bd->status); + if(bd->status & BDLast){ + if(p->mode & (SccHDLC|SccAHDLC)){ + if(BLEN(pkt) <= 2){ + p->frame++; + goto Discard; + } + pkt->wp -= 2; /* strip CRC */ + } + qpass(p->iq, pkt); + }else + p->partial = pkt; + return; + +Discard: + if(pkt != nil) + freeb(pkt); +} + +/* + * handle an interrupt to a single uart + */ +static void +uartintr(Uart *p, int events) +{ + int dokick; + BD *bd; + Block *b; + + if(events & BSY) + p->overrun++; + p->interrupts++; + dokick = 0; + while(p->rxb != nil && ((bd = &p->rxb[p->rdrx])->status & BDEmpty) == 0){ + dcinval(KADDR(bd->addr), bd->length); + if(p->mode) + framedinput(p, bd); + else if(uartinput(p, bd)) + dokick = 1; + bd->status = (bd->status & BDWrap) | BDEmpty|BDInt; + eieio(); + if(++p->rdrx >= Nbuf) + p->rdrx = 0; + } + if((bd = p->txb) != nil){ + if((bd->status & BDReady) == 0){ + ilock(&p->plock); + if((b = p->outb) != nil){ + b->rp += bd->length; + if(b->rp >= b->wp){ + p->outb = nil; + freeb(b); + } + } + txstart(p); + iunlock(&p->plock); + } + } + eieio(); + /* TO DO: modem status isn't available on 82xFADS */ + if(dokick && p->cts && !p->blocked){ + if(p->outb == nil){ + ilock(&p->plock); + txstart(p); + iunlock(&p->plock); + } + cpmop(p->cpm, RestartTx, 0); + } else if (events & TXE) + cpmop(p->cpm, RestartTx, 0); +} + +/* + * used to ensure uart console output when debugging + */ +void +uartwait(void) +{ + Uart *p = uart[0]; + int s; + + while(p && (p->outb||qlen(p->oq))){ + if(islo()) + continue; + s = splhi(); + if((p->txb->status & BDReady) == 0){ + p->blocked = 0; + p->cts = 1; + if(p->scc == nil) + smcuintr(nil, p); + else + sccuintr(nil, p); + } + splx(s); + } +} + +static Dirtab *uartdir; +static int ndir; + +static void +setlength(int i) +{ + Uart *p; + + if(i >= 0){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+4*i].length = qlen(p->iq); + } else for(i = 0; i < nuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+4*i].length = qlen(p->iq); + } + +} + +void +uartinstall(void) +{ + static int already; + int i, n; + char name[2*KNAMELEN]; + if(already) + return; + already = 1; + n = 0; + for(i=0; i<2; i++) + if(conf.smcuarts & (1<<i)){ + snprint(name, sizeof(name), "eia%d", n++); + uartsetup(i+1, CPsmc1+i, name); + } + n = 2; + for(i=0; i<conf.nscc; i++) + if(conf.sccuarts & (1<<i)){ + snprint(name, sizeof(name), "eia%d", n++); + uartsetup(i+1, CPscc1+i, name); + } +} + +/* + * all uarts must be uartsetup() by this point or inside of uartinstall() + */ +static void +uartreset(void) +{ + int i; + Dirtab *dp; + + uartinstall(); /* architecture specific */ + + ndir = 1+4*nuart; + uartdir = xalloc(ndir * sizeof(Dirtab)); + dp = uartdir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + for(i = 0; i < nuart; i++){ + /* 4 directory entries per port */ + strcpy(dp->name, uart[i]->name); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%sctl", uart[i]->name); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%sstatus", uart[i]->name); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + sprint(dp->name, "%smode", uart[i]->name); + dp->qid.path = NETQID(i, Ntypeqid); + dp->perm = 0660; + dp++; + } +} + +static Chan* +uartattach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +uartwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, uartdir, ndir, devgen); +} + +static int +uartstat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, uartdir, ndir, devgen); +} + +static Chan* +uartopen(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, uartdir, ndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0){ + uartenable(p); + qreopen(p->iq); + qreopen(p->oq); + } + qunlock(p); + break; + } + + return c; +} + +static void +uartclose(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + uartdisable(p); + qclose(p->iq); + qclose(p->oq); + } + qunlock(p); + break; + } +} + +static long +uartstatus(Chan*, Uart *p, void *buf, long n, long offset) +{ + IMM *io; + char str[256]; + +// TO DO: change to standard format for first line: +//"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n" + sprint(str, "opens %d ferr %lud oerr %lud crcerr %lud baud %ud perr %lud intr %lud", p->opens, + p->frame, p->overrun, p->crcerr, p->baud, p->perror, p->interrupts); + /* TO DO: cts, dsr, ring, dcd, dtr, rts aren't all available on 82xFADS */ + io = m->iomem; + if(p->scc){ + if((io->pcdat & SIBIT(9)) == 0) + strcat(str, " cts"); + if((io->pcdat & SIBIT(8)) == 0) + strcat(str, " dcd"); + if((io->pbdat & IBIT(22)) == 0) + strcat(str, " dtr"); + }else if(p->smc){ + if((io->pbdat & IBIT(23)) == 0) + strcat(str, " dtr"); + } + strcat(str, "\n"); + return readstr(offset, buf, n, str); +} + +static long +uartread(Chan *c, void *buf, long n, vlong offset) +{ + Uart *p; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, uartdir, ndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return uartstatus(c, p, buf, n, offset); + case Ntypeqid: + return readnum(offset, buf, n, p->mode, NUMSIZE); + } + + return 0; +} + +static Block* +uartbread(Chan *c, long n, ulong offset) +{ + if(c->qid.type & QTDIR || NETTYPE(c->qid.path) != Ndataqid) + return devbread(c, n, offset); + return qbread(uart[NETID(c->qid.path)]->iq, n); +} + +static void +uartctl(Uart *p, char *cmd) +{ + int i, n; + + /* let output drain for a while */ + for(i = 0; i < 16 && qlen(p->oq); i++) + tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125); + + if(strncmp(cmd, "break", 5) == 0){ + uartbreak(p, 0); + return; + } + + n = atoi(cmd+1); + switch(*cmd){ + case 'B': + case 'b': + uartsetbaud(p, n); + break; + case 'D': + case 'd': + uartdtr(p, n); + break; + case 'f': + case 'F': + qflush(p->oq); + break; + case 'H': + case 'h': + qhangup(p->iq, 0); + qhangup(p->oq, 0); + break; + case 'L': + case 'l': + uartbits(p, n); + break; + case 'm': + case 'M': + uartmflow(p, n); + break; + case 'n': + case 'N': + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + uartparity(p, *(cmd+1)); + break; + case 'K': + case 'k': + uartbreak(p, n); + break; + case 'R': + case 'r': + uartrts(p, n); + break; + case 'Q': + case 'q': + qsetlimit(p->iq, n); + qsetlimit(p->oq, n); + break; + case 'W': + case 'w': + /* obsolete */ + break; + case 'X': + case 'x': + p->xonoff = n; + break; + case 'Z': + case 'z': + p->loopback = n; + break; + } +} + +static long +uartwrite(Chan *c, void *buf, long n, vlong offset) +{ + Uart *p; + char cmd[32]; + int m, inuse; + + USED(offset); + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qwrite(p->oq, buf, n); + case Nctlqid: + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + uartctl(p, cmd); + return n; + case Ntypeqid: + if(p->smc || p->putc) + error(Ebadarg); + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + m = strtoul(cmd, nil, 0); + inuse = 0; + qlock(p); + if(p->opens == 0){ + p->mode = m & 0x7F; + p->loopback = (m&0x80)!=0; + p->setup = 0; + }else + inuse = 1; + qunlock(p); + if(inuse) + error(Einuse); + return n; + } +} + +static long +uartbwrite(Chan *c, Block *bp, ulong offset) +{ + if(c->qid.type & QTDIR || NETTYPE(c->qid.path) != Ndataqid) + return devbwrite(c, bp, offset); + return qbwrite(uart[NETID(c->qid.path)]->oq, bp); +} + +static int +uartwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &uartdir[1+4 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(d.mode != ~0UL){ + d.mode &= 0666; + dt[0].perm = dt[1].perm = d.mode; + } + return n; +} + +Dev uartdevtab = { + 't', + "uart", + + uartreset, + devinit, + devshutdown, + uartattach, + uartwalk, + uartstat, + uartopen, + devcreate, + uartclose, + uartread, + uartbread, + uartwrite, + uartbwrite, + devremove, + uartwstat, +}; diff --git a/os/mpc/dsp.c b/os/mpc/dsp.c new file mode 100644 index 00000000..ffe906e9 --- /dev/null +++ b/os/mpc/dsp.c @@ -0,0 +1,289 @@ +/* + * DSP support functions + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +#include "dsp.h" + +enum { + Ndsp = 2, /* determined by hardware */ + + NHOLES= 64 +}; + +typedef struct DSPparam DSPparam; +struct DSPparam { + ulong fdbase; /* function descriptor table physical base address */ + ulong fd_ptr; /* function descriptor pointer */ + ulong dstate; /* DSP state */ + ulong resvd[2]; + ushort dstatus; /* current function descriptor status */ + ushort i; /* number of iterations */ + ushort tap; /* number of TAPs */ + ushort cbase; + ushort anon1; /* sample buffer size-1 */ + ushort xptr; /* pointer to sample */ + ushort anon2; /* output buffer size-1 */ + ushort yptr; /* pointer to output */ + ushort m; /* sample buffer size-1 */ + ushort anon3; /* sample buffer pointer */ + ushort n; /* output buffer size -1 */ + ushort anon4; /* output buffer pointer */ + ushort k; /* coefficient buffer size - 1 */ + ushort anon5; /* coefficient buffer pointer */ +}; + +struct DSP { + Lock; /* protects state */ + void (*done)(void*); + void* arg; + DSPparam* par; + CPMdev* cpm; + + QLock; /* protects busyr */ + int busy; + Rendez busyr; +}; + +static DSP dsps[Ndsp]; +static Lock dsplock; +static int dspinit; +static struct { + QLock; + ulong avail; + Rendez wantr; +} dspalloc; + +static Map fndmapv[NHOLES]; +static RMap fndmap = {"DSP function descriptors"}; + +static void +dspinterrupt(Ureg*, void*) +{ + int i; + ushort events; + DSP *dsp; + + events = m->iomem->sdsr; + m->iomem->sdsr = events; + if(events & (1<<7)) + panic("dsp: SDMA channel bus error sdar=#%lux", m->iomem->sdar); + for(i=0; i<Ndsp; i++) + if(events & (1<<i)){ + dsp = &dsps[i]; + if(dsp->busy){ + dsp->busy = 0; + if(dsp->done) + dsp->done(dsp->arg); + else + wakeup(&dsp->busyr); + }else + print("dsp%d: empty interrupt\n", i); + } +} + +/* + * called by system initialisation to set up the DSPs + */ +void +dspinitialise(void) +{ + CPMdev *d; + + ilock(&dsplock); + if(dspinit == 0){ + mapinit(&fndmap, fndmapv, sizeof(fndmapv)); + d = cpmdev(CPdsp1); + dsps[0].cpm = d; + dsps[0].par = d->param; + d = cpmdev(CPdsp2); + dsps[1].cpm = d; + dsps[1].par = d->param; + intrenable(VectorCPIC+d->irq, dspinterrupt, nil, BUSUNKNOWN, "dsp"); + dspalloc.avail = (1<<Ndsp)-1; + dspinit = 1; + } + iunlock(&dsplock); +} + +static int +dspavail(void*) +{ + return dspalloc.avail != 0; +} + +/* + * wait for a DSP to become available, and return a reference to it. + * if done is not nil, it will be called (with the given arg) when that + * DSP completes each function (if set to interrupt). + */ +DSP* +dspacquire(void (*done)(void*), void *arg) +{ + DSP *dsp; + int i; + + if(dspinit == 0) + dspinitialise(); + qlock(&dspalloc); + if(waserror()){ + qunlock(&dspalloc); + nexterror(); + } + for(i=0;; i++){ + if(i >= Ndsp){ + sleep(&dspalloc.wantr, dspavail, nil); + i = 0; + } + if(dspalloc.avail & (1<<i)) + break; + } + dsp = &dsps[i]; + if(dsp->busy) + panic("dspacquire"); + dsp->done = done; + dsp->arg = arg; + poperror(); + qunlock(&dspalloc); + return dsp; +} + +/* + * relinquish access to the given DSP + */ +void +dsprelease(DSP *dsp) +{ + ulong bit; + + if(dsp == nil) + return; + bit = 1 << (dsp-dsps); + if(dspalloc.avail & bit) + panic("dsprelease"); + dspalloc.avail |= bit; + wakeup(&dspalloc.wantr); +} + +/* + * execute f[0] to f[n-1] on the given DSP + */ +void +dspexec(DSP *dsp, FnD *f, ulong n) +{ + dspsetfn(dsp, f, n); + dspstart(dsp); +} + +/* + * set the DSP to execute f[0] to f[n-1] + */ +void +dspsetfn(DSP *dsp, FnD *f, ulong n) +{ + f[n-1].status |= FnWrap; + ilock(dsp); + dsp->par->fdbase = PADDR(f); + iunlock(dsp); + cpmop(dsp->cpm, InitDSP, 0); +} + +/* + * start execution of the preset function(s) + */ +void +dspstart(DSP *dsp) +{ + ilock(dsp); + dsp->busy = 1; + iunlock(dsp); + cpmop(dsp->cpm, StartDSP, 0); +} + +static int +dspdone(void *a) +{ + return ((DSP*)a)->busy; +} + +/* + * wait until the DSP has completed execution + */ +void +dspsleep(DSP *dsp) +{ + sleep(&dsp->busyr, dspdone, dsp); +} + +/* + * allocate n function descriptors + */ +FnD* +fndalloc(ulong n) +{ + ulong a, nb, pgn; + FnD *f; + + if(n == 0) + return nil; + if(dspinit == 0) + dspinitialise(); + nb = n*sizeof(FnD); + while((a = rmapalloc(&fndmap, 0, nb, sizeof(FnD))) != 0){ + /* expected to loop just once, but might lose a race with another dsp user */ + pgn = (nb+BY2PG-1)&~(BY2PG-1); + a = PADDR(xspanalloc(pgn, sizeof(FnD), 0)); + if(a == 0) + return nil; + mapfree(&fndmap, a, pgn); + } + f = KADDR(a); + f[n-1].status = FnWrap; + return f; +} + +/* + * free n function descriptors + */ +void +fndfree(FnD *f, ulong n) +{ + if(f != nil) + mapfree(&fndmap, PADDR(f), n*sizeof(FnD)); +} + +/* + * allocate an IO buffer region in shared memory for use by the DSP + */ +void* +dspmalloc(ulong n) +{ + ulong i; + + n = (n+3)&~4; + i = n; + if(n & (n-1)){ + /* align on a power of two */ + for(i=1; i < n; i <<= 1) + ; + } + return cpmalloc(n, i); /* this seems to be what 16.3.3.2 is trying to say */ +} + +/* + * free DSP buffer memory + */ +void +dspfree(void *p, ulong n) +{ + if(p != nil) + cpmfree(p, (n+3)&~4); +} diff --git a/os/mpc/dsp.h b/os/mpc/dsp.h new file mode 100644 index 00000000..9838757f --- /dev/null +++ b/os/mpc/dsp.h @@ -0,0 +1,62 @@ +/* + * MPC82x/QUICC DSP support + */ + +typedef struct DSP DSP; +typedef struct FnD FnD; + +typedef short Real; +typedef struct Complex Complex; + +struct Complex { + Real im; + Real re; +}; + +struct FnD { + ushort status; + ushort param[7]; +}; + +enum { + FnDsize = 8*2, /* each function descriptor is 8 shorts */ + + /* standard bits in FnD.status */ + FnStop = 1<<15, + FnWrap = 1<<13, + FnInt = 1<<12, + + /* optional bits */ + FnZ = 1<<11, /* FIR[35], MOD */ + FnIALL = 1<<10, /* FIRx */ + FnXinc0 = 0<<8, /* FIRx, IRR */ + FnXinc1 = 1<<8, + FnXinc2 = 2<<8, + FnXinc3 = 3<<8, + FnPC = 1<<7, /* FIRx */ + + + /* DSP functions (table 16-6) */ + FnFIR1 = 0x01, + FnFIR2 = 0x02, + FnFIR3 = 0x03, + FnFIR5 = 0x03, + FnFIR6 = 0x06, + FnIIR = 0x07, + FnMOD = 0x08, + FnDEMOD = 0x09, + FnLMS1 = 0x0A, + FnLMS2 = 0x0B, + FnWADD = 0x0C, +}; + +void dspinitialise(void); +DSP* dspacquire(void (*)(void*), void*); +void dspexec(DSP*, FnD*, ulong); +void* dspmalloc(ulong); +void dspfree(void*, ulong); +void dspsetfn(DSP*, FnD*, ulong); +void dspstart(DSP*); +void dsprelease(DSP*); +FnD* fndalloc(ulong); +void fndfree(FnD*, ulong); diff --git a/os/mpc/etherif.h b/os/mpc/etherif.h new file mode 100644 index 00000000..65b22c40 --- /dev/null +++ b/os/mpc/etherif.h @@ -0,0 +1,37 @@ +enum { + MaxEther = 6, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { +RWlock; /* TO DO */ + ISAConf; /* hardware info */ + int ctlrno; + int tbdf; /* type+busno+devno+funcno */ + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + int encry; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*closed)(Ether*); + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + int pcmslot; /* PCMCIA */ + int fullduplex; /* non-zero if full duplex */ + + Queue* oq; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern int archether(int, Ether*); diff --git a/os/mpc/etherscc.c b/os/mpc/etherscc.c new file mode 100644 index 00000000..8180140e --- /dev/null +++ b/os/mpc/etherscc.c @@ -0,0 +1,528 @@ +/* + * SCCn ethernet + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +enum { + Nrdre = 16, /* receive descriptor ring entries */ + Ntdre = 16, /* transmit descriptor ring entries */ + + Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */ + Bufsize = (Rbsize+7)&~7, /* aligned */ +}; + +enum { + /* ether-specific Rx BD bits */ + RxMiss= 1<<8, + RxeLG= 1<<5, + RxeNO= 1<<4, + RxeSH= 1<<3, + RxeCR= 1<<2, + RxeOV= 1<<1, + RxeCL= 1<<0, + RxError= (RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL), /* various error flags */ + + /* ether-specific Tx BD bits */ + TxPad= 1<<14, /* pad short frames */ + TxTC= 1<<10, /* transmit CRC */ + TxeDEF= 1<<9, + TxeHB= 1<<8, + TxeLC= 1<<7, + TxeRL= 1<<6, + TxeUN= 1<<1, + TxeCSL= 1<<0, + + /* scce */ + RXB= 1<<0, + TXB= 1<<1, + BSY= 1<<2, + RXF= 1<<3, + TXE= 1<<4, + + /* psmr */ + PRO= 1<<9, /* promiscuous mode */ + + /* gsmrl */ + ENR= 1<<5, + ENT= 1<<4, + + /* port A */ + RXD1= SIBIT(15), + TXD1= SIBIT(14), + + /* port B */ + RTS1= IBIT(19), + + /* port C */ + CTS1= SIBIT(11), + CD1= SIBIT(10), +}; + +typedef struct Etherparam Etherparam; +struct Etherparam { + SCCparam; + ulong c_pres; /* preset CRC */ + ulong c_mask; /* constant mask for CRC */ + ulong crcec; /* CRC error counter */ + ulong alec; /* alighnment error counter */ + ulong disfc; /* discard frame counter */ + ushort pads; /* short frame PAD characters */ + ushort ret_lim; /* retry limit threshold */ + ushort ret_cnt; /* retry limit counter */ + ushort mflr; /* maximum frame length reg */ + ushort minflr; /* minimum frame length reg */ + ushort maxd1; /* maximum DMA1 length reg */ + ushort maxd2; /* maximum DMA2 length reg */ + ushort maxd; /* rx max DMA */ + ushort dma_cnt; /* rx dma counter */ + ushort max_b; /* max bd byte count */ + ushort gaddr[4]; /* group address filter */ + ulong tbuf0_data0; /* save area 0 - current frm */ + ulong tbuf0_data1; /* save area 1 - current frm */ + ulong tbuf0_rba0; + ulong tbuf0_crc; + ushort tbuf0_bcnt; + ushort paddr[3]; /* physical address LSB to MSB increasing */ + ushort p_per; /* persistence */ + ushort rfbd_ptr; /* rx first bd pointer */ + ushort tfbd_ptr; /* tx first bd pointer */ + ushort tlbd_ptr; /* tx last bd pointer */ + ulong tbuf1_data0; /* save area 0 - next frame */ + ulong tbuf1_data1; /* save area 1 - next frame */ + ulong tbuf1_rba0; + ulong tbuf1_crc; + ushort tbuf1_bcnt; + ushort tx_len; /* tx frame length counter */ + ushort iaddr[4]; /* individual address filter*/ + ushort boff_cnt; /* back-off counter */ + ushort taddr[3]; /* temp address */ +}; + +typedef struct { + Lock; + int port; + int init; + int active; + SCC* scc; + CPMdev* cpm; + + Ring; + + ulong interrupts; /* statistics */ + ulong deferred; + ulong heartbeat; + ulong latecoll; + ulong retrylim; + ulong underrun; + ulong overrun; + ulong carrierlost; + ulong retrycount; +} Ctlr; + +static int sccid[] = {-1, CPscc1, CPscc2, CPscc3, CPscc4}; + +static void +attach(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ctlr->active = 1; + ctlr->scc->gsmrl |= ENR|ENT; + eieio(); +} + +static void +closed(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + if(ctlr->active){ + sccxstop(ctlr->cpm); + ilock(ctlr); + ctlr->active = 0; + iunlock(ctlr); + } +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + Ctlr *ctlr; + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + if(on || ether->nmaddr) + ctlr->scc->psmr |= PRO; + else + ctlr->scc->psmr &= ~PRO; + iunlock(ctlr); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + Ether *ether; + Ctlr *ctlr; + + USED(addr, on); /* if on, could SetGroupAddress; if !on, it's hard */ + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + if(ether->prom || ether->nmaddr) + ctlr->scc->psmr |= PRO; + else + ctlr->scc->psmr &= ~PRO; + iunlock(ctlr); +} + +static void +txstart(Ether *ether) +{ + int len; + Ctlr *ctlr; + Block *b; + BD *dre; + + ctlr = ether->ctlr; + while(ctlr->ntq < Ntdre-1){ + b = qget(ether->oq); + if(b == 0) + break; + + dre = &ctlr->tdr[ctlr->tdrh]; + if(dre->status & BDReady) + panic("ether: txstart"); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + len = BLEN(b); + dcflush(b->rp, len); + if(ctlr->txb[ctlr->tdrh] != nil) + panic("scc/ether: txstart"); + ctlr->txb[ctlr->tdrh] = b; + if((ulong)b->rp&1) + panic("scc/ether: txstart align"); /* TO DO: ensure alignment */ + dre->addr = PADDR(b->rp); + dre->length = len; + eieio(); + dre->status = (dre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC; + eieio(); + ctlr->scc->todr = 1<<15; /* transmit now */ + eieio(); + ctlr->ntq++; + ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre); + } +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void *arg) +{ + Ether *ether; + int len, events, status; + Ctlr *ctlr; + BD *dre; + Block *b; + + ether = arg; + ctlr = ether->ctlr; + if(!ctlr->active) + return; /* not ours */ + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ + events = ctlr->scc->scce; + eieio(); + ctlr->scc->scce = events; + eieio(); + ctlr->interrupts++; + + if(events & (TXE|BSY|RXB)){ + if(events & RXB) + ctlr->overrun++; + if(events & TXE) + ether->oerrs++; + if(0 || events & TXE) + print("ETHER.SCC#%d: scce = 0x%uX\n", ether->ctlrno, events); + } + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + */ + if(events & (RXF|RXB) || 1){ + dre = &ctlr->rdr[ctlr->rdrx]; + while(((status = dre->status) & BDEmpty) == 0){ + if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){ + if(status & (RxeLG|RxeSH)) + ether->buffs++; + if(status & RxeNO) + ether->frames++; + if(status & RxeCR) + ether->crcs++; + if(status & RxeOV) + ether->overflows++; + //print("eth rx: %ux\n", status); + } + else{ + /* + * We have a packet. Read it in. + */ + len = dre->length-4; + if((b = iallocb(len)) != 0){ + dcinval(KADDR(dre->addr), len); + memmove(b->wp, KADDR(dre->addr), len); + b->wp += len; + etheriq(ether, b, 1); + }else + ether->soverflows++; + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + dre->length = 0; + dre->status = (status & BDWrap) | BDEmpty | BDInt; + eieio(); + + ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); + dre = &ctlr->rdr[ctlr->rdrx]; + } + } + + /* + * Transmitter interrupt: handle anything queued for a free descriptor. + */ + if(events & TXB){ + lock(ctlr); + while(ctlr->ntq){ + dre = &ctlr->tdr[ctlr->tdri]; + status = dre->status; + if(status & BDReady) + break; + if(status & TxeDEF) + ctlr->deferred++; + if(status & TxeHB) + ctlr->heartbeat++; + if(status & TxeLC) + ctlr->latecoll++; + if(status & TxeRL) + ctlr->retrylim++; + if(status & TxeUN) + ctlr->underrun++; + if(status & TxeCSL) + ctlr->carrierlost++; + ctlr->retrycount += (status>>2)&0xF; + b = ctlr->txb[ctlr->tdri]; + if(b == nil) + panic("scce/interrupt: bufp"); + ctlr->txb[ctlr->tdri] = nil; + freeb(b); + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, Ntdre); + } + txstart(ether); + unlock(ctlr); + } + if(events & TXE) + cpmop(ctlr->cpm, RestartTx, 0); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len; + Ctlr *ctlr; + + if(n == 0) + return 0; + + ctlr = ether->ctlr; + + p = malloc(READSTR); + len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); + len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost); + len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat); + len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim); + len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount); + len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll); + len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun); + len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun); + snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred); + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +/* + * This follows the MPC823 user guide: section16.9.23.7's initialisation sequence, + * except that it sets the right bits for the MPC823ADS board when SCC2 is used, + * and those for the 860/821 development board for SCC1. + */ +static void +sccsetup(Ctlr *ctlr, SCC *scc, Ether *ether) +{ + int i, rcs, tcs, w; + Etherparam *p; + IMM *io; + + + i = 2*(ctlr->port-1); + io = ioplock(); + w = (TXD1|RXD1)<<i; /* TXDn and RXDn in port A */ + io->papar |= w; /* enable TXDn and RXDn pins */ + io->padir &= ~w; + io->paodr &= ~w; /* not open drain */ + + w = (CD1|CTS1)<<i; /* CLSN and RENA: CDn and CTSn in port C */ + io->pcpar &= ~w; /* enable CLSN (CTSn) and RENA (CDn) */ + io->pcdir &= ~w; + io->pcso |= w; + iopunlock(); + + /* clocks and transceiver control: details depend on the board's wiring */ + archetherenable(sccid[ctlr->port], &rcs, &tcs, ether->mbps, ether->fullduplex); + + sccnmsi(ctlr->port, rcs, tcs); /* connect the clocks */ + + p = ctlr->cpm->param; + memset(p, 0, sizeof(*p)); + p->rfcr = 0x18; + p->tfcr = 0x18; + p->mrblr = Bufsize; + p->rbase = PADDR(ctlr->rdr); + p->tbase = PADDR(ctlr->tdr); + + cpmop(ctlr->cpm, InitRxTx, 0); + + p->c_pres = ~0; + p->c_mask = 0xDEBB20E3; + p->crcec = 0; + p->alec = 0; + p->disfc = 0; + p->pads = 0x8888; + p->ret_lim = 0xF; + p->mflr = Rbsize; + p->minflr = ETHERMINTU+4; + p->maxd1 = Bufsize; + p->maxd2 = Bufsize; + p->p_per = 0; /* only moderate aggression */ + + for(i=0; i<Eaddrlen; i+=2) + p->paddr[2-i/2] = (ether->ea[i+1]<<8)|ether->ea[i]; /* it's not the obvious byte order */ + + scc->psmr = (2<<10)|(5<<1); /* 32-bit CRC, ignore 22 bits before SFD */ + scc->dsr = 0xd555; + scc->gsmrh = 0; /* normal operation */ + scc->gsmrl = (1<<28)|(4<<21)|(1<<19)|0xC; /* transmit clock invert, 48 bit preamble, repetitive 10 preamble, ethernet */ + eieio(); + scc->scce = ~0; /* clear all events */ + eieio(); + scc->sccm = TXE | RXF | TXB; /* enable interrupts */ + eieio(); + + io = ioplock(); + w = RTS1<<(ctlr->port-1); /* enable TENA pin (RTSn) */ + io->pbpar |= w; + io->pbdir |= w; + iopunlock(); + + /* gsmrl enable is deferred until attach */ +} + +static int +reset(Ether* ether) +{ + uchar ea[Eaddrlen]; + CPMdev *cpm; + Ctlr *ctlr; + SCC *scc; + + if(m->speed < 24){ + print("%s ether: system speed must be >= 24MHz for ether use\n", ether->type); + return -1; + } + + if(!(ether->port >= 1 && ether->port <= 4)){ + print("%s ether: no SCC port %lud\n", ether->type, ether->port); + return -1; + } + + /* + * Insist that the platform-specific code provide the Ethernet address + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + print("no ether address"); + return -1; + } + + cpm = cpmdev(sccid[ether->port]); + if(cpm == nil) + return -1; + ether->irq = VectorCPIC + cpm->irq; + scc = cpm->regs; + ctlr = malloc(sizeof(*ctlr)); + ether->ctlr = ctlr; + memset(ctlr, 0, sizeof(*ctlr)); + ctlr->cpm = cpm; + ctlr->scc = scc; + ctlr->port = ether->port; + + if(ioringinit(ctlr, Nrdre, Ntdre, Bufsize) < 0) + panic("etherscc init"); + + sccsetup(ctlr, scc, ether); + + ether->attach = attach; + ether->closed = closed; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + + return 0; +} + +void +etherscclink(void) +{ + addethercard("SCC", reset); +} diff --git a/os/mpc/faultpower.c b/os/mpc/faultpower.c new file mode 100644 index 00000000..6d3b163b --- /dev/null +++ b/os/mpc/faultpower.c @@ -0,0 +1,49 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" + +enum +{ + MC_IFETCH = (1<<30), + MC_STORE = (1<<11), /* bit 23 if X-form, bit 3 if D-form => write */ + DSI_STORE = (1<<25), + DSI_PROT = (1<<27), +}; + +void +faultpower(Ureg *ur) +{ + ulong addr; + char buf[ERRMAX]; + int read, i; + + addr = ur->pc; /* assume instr. exception */ + read = 1; + i = ur->cause >> 8; + if(i == CDSI || i == CDTLBE || i == CMCHECK && (ur->status&MC_IFETCH) == 0) { /* data access error including machine check load/store */ + addr = getdar(); + if(getdsisr() & (DSI_STORE|MC_STORE)) + read = 0; + } else if(i == CDMISS) /* DTLB miss */ + addr = getdepn() & ~0x3FF; /* can't distinguish read/write, but Inferno doesn't care */ +/* +print("fault %lux %lux %lux %d\n", ur->pc, ur->cause, addr, read); +print("imiss %lux dmiss %lux hash1 %lux dcmp %lux hash2 %lux\n", + getimiss(), getdmiss(), gethash1(), getdcmp(), gethash2()); +print("up %lux %lux %lux\n", m->upage, m->upage->virt, m->upage->phys); +*/ + + up->dbgreg = ur; /* For remote ACID */ + + spllo(); + sprint(buf, "trap: fault %s pc=0x%lux addr=0x%lux", + read ? "read" : "write", ur->pc, addr); + if(up->type == Interp) + disfault(ur, buf); + dumpregs(ur); + panic("fault: %s\n", buf); +} diff --git a/os/mpc/fp.s b/os/mpc/fp.s new file mode 100644 index 00000000..e37bb63a --- /dev/null +++ b/os/mpc/fp.s @@ -0,0 +1,205 @@ +/* + * support for floating-point hardware + */ + +#include "mem.h" + +/* on some models mtmsr doesn't synchronise enough (eg, 603e) */ +#define MSRSYNC SYNC; ISYNC + +#define FPON(X, Y)\ + MOVW MSR, X;\ + OR $FPE, X, Y;\ + SYNC;\ + ISYNC;\ + MOVW Y, MSR;\ + MSRSYNC + +#define FPOFF(X,Y)\ + MOVW MSR, X;\ + RLWNM $0, X, $~FPE, Y;\ + SYNC;\ + ISYNC;\ + MOVW Y, MSR;\ + MSRSYNC + +#define FPPREV(X)\ + SYNC;\ + ISYNC;\ + MOVW X, MSR;\ + MSRSYNC + +TEXT kfpinit(SB), $0 + MOVFL $0,FPSCR(7) + MOVFL $0xD,FPSCR(6) /* VE, OE, ZE */ + MOVFL $0, FPSCR(5) + MOVFL $0, FPSCR(3) + MOVFL $0, FPSCR(2) + MOVFL $0, FPSCR(1) + MOVFL $0, FPSCR(0) + + FMOVD $4503601774854144.0, F27 + FMOVD $0.5, F29 + FSUB F29, F29, F28 + FADD F29, F29, F30 + FADD F30, F30, F31 + FMOVD F28, F0 + FMOVD F28, F1 + FMOVD F28, F2 + FMOVD F28, F3 + FMOVD F28, F4 + FMOVD F28, F5 + FMOVD F28, F6 + FMOVD F28, F7 + FMOVD F28, F8 + FMOVD F28, F9 + FMOVD F28, F10 + FMOVD F28, F11 + FMOVD F28, F12 + FMOVD F28, F13 + FMOVD F28, F14 + FMOVD F28, F15 + FMOVD F28, F16 + FMOVD F28, F17 + FMOVD F28, F18 + FMOVD F28, F19 + FMOVD F28, F20 + FMOVD F28, F21 + FMOVD F28, F22 + FMOVD F28, F23 + FMOVD F28, F24 + FMOVD F28, F25 + FMOVD F28, F26 + RETURN + +TEXT getfpscr(SB), $8 + FPON(R4, R5) + MOVFL FPSCR, F3 + FMOVD F3, -8(SP) + MOVW -4(SP), R3 + FPPREV(R4) + RETURN + +TEXT fpsave(SB), $0 + FPON(R4, R4) + + FMOVD F0,0(R3) + FMOVD F1,8(R3) + FMOVD F2,16(R3) + FMOVD F3,24(R3) + FMOVD F4,32(R3) + FMOVD F5,40(R3) + FMOVD F6,48(R3) + FMOVD F7,56(R3) + FMOVD F8,64(R3) + FMOVD F9,72(R3) + FMOVD F10,80(R3) + FMOVD F11,88(R3) + FMOVD F12,96(R3) + FMOVD F13,104(R3) + FMOVD F14,112(R3) + FMOVD F15,120(R3) + FMOVD F16,128(R3) + FMOVD F17,136(R3) + FMOVD F18,144(R3) + FMOVD F19,152(R3) + FMOVD F20,160(R3) + FMOVD F21,168(R3) + FMOVD F22,176(R3) + FMOVD F23,184(R3) + FMOVD F24,192(R3) + FMOVD F25,200(R3) + FMOVD F26,208(R3) + FMOVD F27,216(R3) + FMOVD F28,224(R3) + FMOVD F29,232(R3) + FMOVD F30,240(R3) + FMOVD F31,248(R3) + + MOVFL FPSCR, F0 + FMOVD F0, 256(R3) + MOVFL $0,FPSCR(7) + MOVFL $0xD,FPSCR(6) /* VE, OE, ZE */ + MOVFL $0, FPSCR(5) + MOVFL $0, FPSCR(4) + MOVFL $0, FPSCR(3) + MOVFL $0, FPSCR(2) + MOVFL $0, FPSCR(1) + MOVFL $0, FPSCR(0) + + FPOFF(R4, R4) + RETURN + +TEXT fprestore(SB), $0 + FPON(R4, R4) + + FMOVD 256(R3), F0 + MOVFL F0, FPSCR + FMOVD 0(R3), F0 + FMOVD 8(R3), F1 + FMOVD 16(R3), F2 + FMOVD 24(R3), F3 + FMOVD 32(R3), F4 + FMOVD 40(R3), F5 + FMOVD 48(R3), F6 + FMOVD 56(R3), F7 + FMOVD 64(R3), F8 + FMOVD 72(R3), F9 + FMOVD 80(R3), F10 + FMOVD 88(R3), F11 + FMOVD 96(R3), F12 + FMOVD 104(R3), F13 + FMOVD 112(R3), F14 + FMOVD 120(R3), F15 + FMOVD 128(R3), F16 + FMOVD 136(R3), F17 + FMOVD 144(R3), F18 + FMOVD 152(R3), F19 + FMOVD 160(R3), F20 + FMOVD 168(R3), F21 + FMOVD 176(R3), F22 + FMOVD 184(R3), F23 + FMOVD 192(R3), F24 + FMOVD 200(R3), F25 + FMOVD 208(R3), F26 + FMOVD 216(R3), F27 + FMOVD 224(R3), F28 + FMOVD 232(R3), F29 + FMOVD 240(R3), F30 + FMOVD 248(R3), F31 + + RETURN + +TEXT clrfptrap(SB), $0 + FPON(R4, R5) + MOVFL $0, FPSCR(5) + MOVFL $0, FPSCR(3) + MOVFL $0, FPSCR(2) + MOVFL $0, FPSCR(1) + MOVFL $0, FPSCR(0) + FPPREV(R4) + RETURN + +TEXT fpinit(SB), $0 + FPON(R4, R5) + BL kfpinit(SB) + RETURN + +TEXT fpoff(SB), $0 + FPOFF(R4, R5) + RETURN + + +TEXT FPsave(SB), 1, $0 + FPON(R4, R5) + MOVFL FPSCR, F0 + FMOVD F0, 0(R3) + FPPREV(R4) + RETURN + +TEXT FPrestore(SB), 1, $0 + FPON(R4, R5) + FMOVD 0(R3), F0 + MOVFL F0, FPSCR + FPPREV(R4) + RETURN diff --git a/os/mpc/fpi.h b/os/mpc/fpi.h new file mode 100644 index 00000000..dfb9b1df --- /dev/null +++ b/os/mpc/fpi.h @@ -0,0 +1,61 @@ +typedef long Word; +typedef unsigned long Single; +typedef struct { + unsigned long h; + unsigned long l; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<<NGuardBits), + + SingleExpBias = 127, + SingleExpMax = 255, + DoubleExpBias = 1023, + DoubleExpMax = 2047, + + ExpBias = DoubleExpBias, + ExpInfinity = DoubleExpMax, +}; + +typedef struct { + unsigned char s; + short e; + long l; /* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */ + long h; /* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */ +} Internal; + +#define IsWeird(n) ((n)->e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff --git a/os/mpc/fpipower.c b/os/mpc/fpipower.c new file mode 100644 index 00000000..ec4363b2 --- /dev/null +++ b/os/mpc/fpipower.c @@ -0,0 +1,970 @@ +/* + * this doesn't attempt to implement Power architecture floating-point properties + * that aren't visible in the Inferno environment. + * all arithmetic is done in double precision. + * the FP trap status isn't updated. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +#include "fpi.h" + +#define REG(x) (*(long*)(((char*)em->ur)+roff[(x)])) +#define FR(x) (*(Internal*)em->fr[(x)&0x1F]) +#define REGSP 1 /* stack pointer */ + +/* BUG: check fetch (not worthwhile in Inferno) */ +#define getulong(a) (*(ulong*)(a)) + +enum { + CRLT = 1<<31, + CRGT = 1<<30, + CREQ = 1<<29, + CRSO = 1<<28, + CRFU = CRSO, + + CRFX = 1<<27, + CRFEX = 1<<26, + CRVX = 1<<25, + CROX = 1<<24, +}; + +#define getCR(x,w) (((w)>>(28-(x*4)))&0xF) +#define mkCR(x,v) (((v)&0xF)<<(28-(x*4))) + +#define simm(xx, ii) xx = (short)(ii&0xFFFF); +#define getairr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; simm(imm,i) +#define getarrr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; rb = (i>>11)&0x1f; +#define getop(i) ((i>>26)&0x3F) +#define getxo(i) ((i>>1)&0x3FF) + +#define FPS_FX (1<<31) /* exception summary (sticky) */ +#define FPS_EX (1<<30) /* enabled exception summary */ +#define FPS_VX (1<<29) /* invalid operation exception summary */ +#define FPS_OX (1<<28) /* overflow exception OX (sticky) */ +#define FPS_UX (1<<27) /* underflow exception UX (sticky) */ +#define FPS_ZX (1<<26) /* zero divide exception ZX (sticky) */ +#define FPS_XX (1<<25) /* inexact exception XX (sticky) */ +#define FPS_VXSNAN (1<<24) /* invalid operation exception for SNaN (sticky) */ +#define FPS_VXISI (1<<23) /* invalid operation exception for ∞-∞ (sticky) */ +#define FPS_VXIDI (1<<22) /* invalid operation exception for ∞/∞ (sticky) */ +#define FPS_VXZDZ (1<<21) /* invalid operation exception for 0/0 (sticky) */ +#define FPS_VXIMZ (1<<20) /* invalid operation exception for ∞*0 (sticky) */ +#define FPS_VXVC (1<<19) /* invalid operation exception for invalid compare (sticky) */ +#define FPS_FR (1<<18) /* fraction rounded */ +#define FPS_FI (1<<17) /* fraction inexact */ +#define FPS_FPRF (1<<16) /* floating point result class */ +#define FPS_FPCC (0xF<<12) /* <, >, =, unordered */ +#define FPS_VXCVI (1<<8) /* enable exception for invalid integer convert (sticky) */ +#define FPS_VE (1<<7) /* invalid operation exception enable */ +#define FPS_OE (1<<6) /* enable overflow exceptions */ +#define FPS_UE (1<<5) /* enable underflow */ +#define FPS_ZE (1<<4) /* enable zero divide */ +#define FPS_XE (1<<3) /* enable inexact exceptions */ +#define FPS_RN (3<<0) /* rounding mode */ + +typedef struct Emreg Emreg; + +struct Emreg { + Ureg* ur; + ulong (*fr)[3]; + FPenv* ufp; + ulong ir; + char* name; +}; + +int fpemudebug = 0; + +#undef OFR +#define OFR(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + OFR(r0), OFR(r1), OFR(r2), OFR(r3), + OFR(r4), OFR(r5), OFR(r6), OFR(r7), + OFR(r8), OFR(r9), OFR(r10), OFR(r11), + OFR(r12), OFR(r13), OFR(r14), OFR(r15), + OFR(r16), OFR(r17), OFR(r18), OFR(r19), + OFR(r20), OFR(r21), OFR(r22), OFR(r23), + OFR(r24), OFR(r25), OFR(r26), OFR(r27), + OFR(r28), OFR(r29), OFR(r30), OFR(r31), +}; + +/* + * initial FP register values assumed by qc's code + */ +static Internal fpreginit[] = { + /* s, e, l, h */ + {0, 0x400, 0x00000000, 0x08000000}, /* F31=2.0 */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* F30=1.0 */ + {0, 0x3FE, 0x00000000, 0x08000000}, /* F29=0.5 */ + {0, 0x1, 0x00000000, 0x00000000}, /* F28=0.0 */ + {0, 0x433, 0x00000000, 0x08000040}, /* F27=FREGCVI */ +}; + +static void +fadd(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + (a.s == b.s? fpiadd: fpisub)(&b, &a, d); +} + +static void +fsub(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + b.s ^= 1; + (b.s == a.s? fpiadd: fpisub)(&b, &a, d); +} + +static void +fmul(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + fpimul(&b, &a, d); +} + +static void +fdiv(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + fpidiv(&b, &a, d); +} + +static void +fmsub(Emreg *em, Internal *d, int ra, int rc, int rb) +{ + Internal a, c, b, t; + + a = FR(ra); + c = FR(rc); + b = FR(rb); + fpimul(&a, &c, &t); + b.s ^= 1; + (b.s == t.s? fpiadd: fpisub)(&b, &t, d); +} + +static void +fmadd(Emreg *em, Internal *d, int ra, int rc, int rb) +{ + Internal a, c, b, t; + + a = FR(ra); + c = FR(rc); + b = FR(rb); + fpimul(&a, &c, &t); + (t.s == b.s? fpiadd: fpisub)(&b, &t, d); +} + +static ulong setfpscr(Emreg*); +static void setfpcc(Emreg*, int); + +static void +unimp(Emreg *em, ulong op) +{ + char buf[60]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", em->ur->pc, op); + if(fpemudebug) + print("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +/* + * floating load/store + */ + +static void +fpeairr(Emreg *em, ulong ir, void **eap, int *rdp) +{ + ulong ea; + long imm; + int ra, rd, upd; + + getairr(ir); + ea = imm; + upd = (ir&(1L<<26))!=0; + if(ra) { + ea += REG(ra); + if(upd){ + if(ra == REGSP) + panic("fpemu: r1 update"); /* can't do it because we're running on the same stack */ + REG(ra) = ea; + } + } else { + if(upd) + unimp(em, ir); + } + *rdp = rd; + *eap = (void*)ea; + if(fpemudebug) + print("%8.8lux %s\tf%d,%ld(r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, imm, ra, ea, upd); +} + +static void +fpearrr(Emreg *em, ulong ir, int upd, void **eap, int *rdp) +{ + ulong ea; + int ra, rb, rd; + + getarrr(ir); + ea = REG(rb); + if(ra){ + ea += REG(ra); + if(upd){ + if(ra == REGSP) + panic("fpemu: r1 update"); + REG(ra) = ea; + } + if(fpemudebug) + print("%8.8lux %s\tf%d,(r%d+r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, ra, rb, ea, upd); + } else { + if(upd) + unimp(em, ir); + if(fpemudebug) + print("%8.8lux %s\tf%d,(r%d) ea=%lux\n", em->ur->pc, em->name, rd, rb, ea); + } + *eap = (void*)ea; + *rdp = rd; +} + +static void +lfs(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfs"; + fpeairr(em, ir, &ea, &rd); + fpis2i(&FR(rd), (void*)ea); +} + +static void +lfsx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfsx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==567, &ea, &rd); + fpis2i(&FR(rd), (void*)ea); +} + +static void +lfd(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfd"; + fpeairr(em, ir, &ea, &rd); + fpid2i(&FR(rd), (void*)ea); +} + +static void +lfdx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfdx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==631, &ea, &rd); + fpid2i(&FR(rd), (void*)ea); +} + +static void +stfs(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfs"; + fpeairr(em, ir, &ea, &rd); + tmp = FR(rd); + fpii2s(ea, &tmp); +} + +static void +stfsx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfsx"; + fpearrr(em, ir, getxo(ir)==695, &ea, &rd); + tmp = FR(rd); + fpii2s(ea, &tmp); +} + +static void +stfd(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfd"; + fpeairr(em, ir, &ea, &rd); + tmp = FR(rd); + fpii2d(ea, &tmp); +} + +static void +stfdx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfdx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==759, &ea, &rd); + tmp = FR(rd); + fpii2d(ea, &tmp); +} + +static void +mcrfs(Emreg *em, ulong ir) +{ + int rd, ra, rb; + static ulong fpscr0[] ={ + FPS_FX|FPS_OX, + FPS_UX|FPS_ZX|FPS_XX|FPS_VXSNAN, + FPS_VXISI|FPS_VXIDI|FPS_VXZDZ|FPS_VXIMZ, + FPS_VXVC, + 0, + FPS_VXCVI, + }; + + getarrr(ir); + if(rb || ra&3 || rd&3) + unimp(em, ir); + ra >>= 2; + rd >>= 2; + em->ur->cr = (em->ur->cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, em->ufp->fpscr)); + em->ufp->fpscr &= ~fpscr0[ra]; + if(fpemudebug) + print("%8.8lux mcrfs\tcrf%d,crf%d\n", em->ur->pc, rd, ra); +} + +static void +mffs(Emreg *em, ulong ir) +{ + int rd, ra, rb; + Double dw; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + dw.h = 0; + dw.l = ((uvlong)0xFFF8000L<<16)|em->ufp->fpscr; + fpid2i(&FR(rd), &dw); + /* it's anyone's guess how CR1 should be set when ir&1 */ + em->ur->cr &= ~mkCR(1, 0xE); /* leave SO, reset others */ + if(fpemudebug) + print("%8.8lux mffs%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsb1(Emreg *em, ulong ir) +{ + int rd, ra, rb; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + em->ufp->fpscr |= (1L << (31-rd)); + /* BUG: should set summary bits */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsb1%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsb0(Emreg *em, ulong ir) +{ + int rd, ra, rb; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + em->ufp->fpscr &= ~(1L << (31-rd)); + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsb0%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsf(Emreg *em, ulong ir) +{ + int fm, rb, i; + ulong v; + Internal b; + Double db; + + if(ir & ((1L << 25)|(1L << 16))) + unimp(em, ir); + rb = (ir >> 11) & 0x1F; + fm = (ir >> 17) & 0xFF; + b = FR(rb); + fpii2d(&db, &b); /* reconstruct hi/lo format to recover low word */ + v = db.l; + for(i=0; i<8; i++) + if(fm & (1 << (7-i))) + em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v)); + /* BUG: should set FEX and VX `according to the usual rule' */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsf%s\t#%.2x,fr%d\n", em->ur->pc, ir&1?".":"", fm, rb); +} + +static void +mtfsfi(Emreg *em, ulong ir) +{ + int imm, rd; + + if(ir & ((0x7F << 16)|(1L << 11))) + unimp(em, ir); + rd = (ir >> 23) & 0xF; + imm = (ir >> 12) & 0xF; + em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm); + /* BUG: should set FEX and VX `according to the usual rule' */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsfi%s\tcrf%d,#%x\n", em->ur->pc, ir&1?".":"", rd, imm); +} + +static void +fcmp(Emreg *em, ulong ir) +{ + int fc, rd, ra, rb, sig, i; + + getarrr(ir); + if(rd & 3) + unimp(em, ir); + rd >>= 2; + sig = 0; + switch(getxo(ir)) { + default: + unimp(em, ir); + case 32: + if(fpemudebug) + print("%8.8lux fcmpo\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); + sig = 1; + break; + case 0: + if(fpemudebug) + print("%8.8lux fcmpu\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); + break; + } + if(IsWeird(&FR(ra)) || IsWeird(&FR(rb))) { + if(sig){ + ; /* BUG: should trap if not masked ... */ + } + fc = CRFU; + } else { + i = fpicmp(&FR(ra), &FR(rb)); + if(i > 0) + fc = CRGT; + else if(i == 0) + fc = CREQ; + else + fc = CRLT; + } + fc >>= 28; + em->ur->cr = (em->ur->cr & ~mkCR(rd,~0)) | mkCR(rd, fc); + em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (fc<<11); + /* BUG: update FX, VXSNAN, VXVC */ +} + +static void +fariths(Emreg *em, ulong ir) +{ + int rd, ra, rb, rc, fmt; + char *cc, *n; + ulong fpscr; + Internal *d; + + fmt = 0; + rc = (ir>>6)&0x1F; + getarrr(ir); + d = &FR(rd); + switch(getxo(ir)&0x1F) { /* partial XO decode */ + case 22: /* fsqrts */ + case 24: /* fres */ + default: + unimp(em, ir); + return; + case 18: + if(IsZero(&FR(rb))) { + em->ufp->fpscr |= FPS_ZX | FPS_FX; + error("sys: fp: zero divide"); + } + fdiv(em, d, ra, rb); + n = "fdivs"; + break; + case 20: + fsub(em, d, ra, rb); + n = "fsubs"; + break; + case 21: + fadd(em, d, ra, rb); + n = "fadds"; + break; + case 25: + fmul(em, d, ra, rc); + rb = rc; + n = "fmuls"; + break; + case 28: + fmsub(em, d, ra, rc, rb); + fmt = 2; + n = "fmsubs"; + break; + case 29: + fmadd(em, d, ra, rc, rb); + fmt = 2; + n = "fmadds"; + break; + case 30: + fmsub(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmsubs"; + break; + case 31: + fmadd(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmadds"; + break; + } + if(fmt==1 && ra) + unimp(em, ir); + fpscr = setfpscr(em); + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) { + switch(fmt) { + case 0: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); + break; + case 1: + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); + break; + case 2: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); + break; + } + } +} + +static void +farith(Emreg *em, ulong ir) +{ + Word w; + Double dv; + int rd, ra, rb, rc, fmt; + char *cc, *n; + ulong fpscr; + int nocc; + Internal *d; + + fmt = 0; + nocc = 0; + rc = (ir>>6)&0x1F; + getarrr(ir); + d = &FR(rd); + switch(getxo(ir)&0x1F) { /* partial XO decode */ + case 22: /* frsqrt */ + case 23: /* fsel */ + case 26: /* fsqrte */ + default: + unimp(em, ir); + return; + case 12: /* frsp */ + *d = FR(rb); /* BUG: doesn't round to single precision */ + fmt = 1; + n = "frsp"; + break; + case 14: /* fctiw */ /* BUG: ignores rounding mode */ + case 15: /* fctiwz */ + fpii2w(&w, &FR(rb)); + dv.h = 0; + dv.l = w; + fpid2i(d, &dv); + fmt = 1; + nocc = 1; + n = "fctiw"; + break; + case 18: + if(IsZero(&FR(rb))) { + em->ufp->fpscr |= FPS_ZX | FPS_FX; + error("sys: fp: zero divide"); + } + fdiv(em, d, ra, rb); + n = "fdiv"; + break; + case 20: + fsub(em, d, ra, rb); + n = "fsub"; + break; + case 21: + fadd(em, d, ra, rb); + n = "fadd"; + break; + case 25: + fmul(em, d, ra, rc); + rb = rc; + n = "fmul"; + break; + case 28: + fmsub(em, d, ra, rc, rb); + fmt = 2; + n = "fmsub"; + break; + case 29: + fmadd(em, d, ra, rc, rb); + fmt = 2; + n = "fmadd"; + break; + case 30: + fmsub(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmsub"; + break; + case 31: + fmadd(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmadd"; + break; + } + if(fmt==1 && ra) + unimp(em, ir); + fpscr = setfpscr(em); + if(nocc == 0) + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) { + switch(fmt) { + case 0: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); + break; + case 1: + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); + break; + case 2: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); + break; + } + } +} + +static void +farith2(Emreg *em, ulong ir) +{ + int rd, ra, rb; + char *cc, *n; + ulong fpscr; + Internal *d, *b; + + getarrr(ir); + if(ra) + unimp(em, ir); + d = &FR(rd); + b = &FR(rb); + switch(getxo(ir)) { /* full XO decode */ + default: + unimp(em, ir); + case 40: + *d = *b; + d->s ^= 1; + n = "fneg"; + break; + case 72: + *d = *b; + n = "fmr"; + break; + case 136: + *d = *b; + d->s = 1; + n = "fnabs"; + break; + case 264: + *d = *b; + d->s = 0; + n = "fabs"; + break; + } + fpscr = setfpscr(em); + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); +} + +static ulong +setfpscr(Emreg *em) +{ + ulong fps, fpscr; + + fps = 0; /* BUG: getfsr() */ + fpscr = em->ufp->fpscr; + if(fps & FPAOVFL) + fpscr |= FPS_OX; + if(fps & FPAINEX) + fpscr |= FPS_XX; + if(fps & FPAUNFL) + fpscr |= FPS_UX; + if(fps & FPAZDIV) + fpscr |= FPS_ZX; + if(fpscr != em->ufp->fpscr) { + fpscr |= FPS_FX; + em->ufp->fpscr = fpscr; + } + return fpscr; +} + +static void +setfpcc(Emreg *em, int r) +{ + int c; + Internal *d; + + d = &FR(r); + c = 0; + if(IsZero(d)) + c |= 2; + else if(d->s == 1) + c |= 4; + else + c |= 8; + if(IsNaN(d)) + c |= 1; + em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */ +} + +static uchar op63flag[32] = { +[12] 1, [14] 1, [15] 1, [18] 1, [20] 1, [21] 1, [22] 1, +[23] 1, [25] 1, [26] 1, [28] 1, [29] 1, [30] 1, [31] 1, +}; + +/* + * returns the number of FP instructions emulated + */ +int +fpipower(Ureg *ur) +{ + ulong op; + int xo; + Emreg emreg, *em; + FPenv *ufp; + int n; + + ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ + em = &emreg; + em->ur = ur; + em->fr = ufp->emreg; + em->ufp = ufp; + em->name = nil; + if(em->ufp->fpistate != FPACTIVE) { + em->ufp->fpistate = FPACTIVE; + em->ufp->fpscr = 0; /* TO DO */ + for(n = 0; n < nelem(fpreginit); n++) + FR(31-n) = fpreginit[n]; + } + for(n=0;;n++){ + op = getulong(ur->pc); + em->ir = op; + if(fpemudebug > 1) + print("%8.8lux %8.8lux: ", ur->pc, op); + switch(op>>26){ + default: + return n; + case 48: /* lfs */ + case 49: /* lfsu */ + lfs(em, op); + break; + case 50: /* lfd */ + case 51: /* lfdu */ + lfd(em, op); + break; + case 52: /* stfs */ + case 53: /* stfsu */ + stfs(em, op); + break; + case 54: /* stfd */ + case 55: /* stfdu */ + stfd(em, op); + break; + case 31: /* indexed load/store */ + xo = getxo(op); + if((xo & 0x300) != 0x200) + return n; + switch(xo){ + default: + return n; + case 535: /* lfsx */ + case 567: /* lfsux */ + lfsx(em, op); + break; + case 599: /* lfdx */ + case 631: /* lfdux */ + lfdx(em, op); + break; + case 663: /* stfsx */ + case 695: /* stfsux */ + stfsx(em, op); + break; + case 727: /* stfdx */ + case 759: /* stfdux */ + stfdx(em, op); + break; + } + break; + case 63: /* double precision */ + xo = getxo(op); + if(op63flag[xo & 0x1F]){ + farith(em, op); + break; + } + switch(xo){ + default: + return n; + case 0: /* fcmpu */ + case 32: /* fcmpo */ + fcmp(em, op); + break; + case 40: /* fneg */ + case 72: /* fmr */ + case 136: /* fnabs */ + case 264: /* fabs */ + farith2(em, op); + break; + case 38: + mtfsb1(em, op); + break; + case 64: + mcrfs(em, op); + break; + case 70: + mtfsb0(em, op); + break; + case 134: + mtfsfi(em, op); + break; + case 583: + mffs(em, op); + break; + case 711: + mtfsf(em, op); + break; + } + break; + case 59: /* single precision */ + fariths(em, op); + break; + } + ur->pc += 4; + if(anyhigher()) + sched(); + } + return n; +} + +/* +50: lfd frD,d(rA) +51: lfdu frD,d(rA) +31,631: lfdux frD,rA,rB +31,599: lfdx frD,rA,rB +48: lfs frD,d(rA) +49: lfsu frD,d(rA) +31,567: lfsux frD,rA,rB +31,535: lfsx frD,rA,rB + +54: stfd frS,d(rA) +55: stfdu frS,d(rA) +31,759: stfdux frS,rA,rB +31,727: stfdx frS,rA,rB +52: stfs frS,d(rA) +53: stfsu frS,d(rA) +31,695: stfsux frS,rA,rB +31,663: stfsx frS,rA,rB + +63,64: mcrfs crfD,crfS +63,583: mffs[.] frD +63,70: mtfsb0[.] crbD +63,38: mtfsb1[.] crbD +63,711: mtfsf[.] FM,frB +63,134: mtfsfi[.] crfD,IMM +*/ + +/* +float to int: + FMOVD g+0(SB),F1 + FCTIWZ F1,F4 + FMOVD F4,.rathole+0(SB) + MOVW .rathole+4(SB),R7 + MOVW R7,l+0(SB) +*/ + +/* +int to float: + MOVW $1127219200,R9 + MOVW l+0(SB),R7 + MOVW R9,.rathole+0(SB) + XOR $-2147483648,R7,R6 + MOVW R6,.rathole+4(SB) + FMOVD .rathole+0(SB),F0 + FSUB F27,F0 + +unsigned to float: + MOVW ul+0(SB),R5 + MOVW R9,.rathole+0(SB) + XOR $-2147483648,R5,R4 + MOVW R4,.rathole+4(SB) + FMOVD .rathole+0(SB),F3 + FSUB F27,F3 + FCMPU F3,F28 + BGE ,3(PC) + FMOVD $4.29496729600000000e+09,F2 + FADD F2,F3 + FMOVD F3,g+0(SB) +*/ diff --git a/os/mpc/i2c.c b/os/mpc/i2c.c new file mode 100644 index 00000000..5218b1c0 --- /dev/null +++ b/os/mpc/i2c.c @@ -0,0 +1,439 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * basic read/write interface to mpc8xx I2C bus (master mode) + */ + +typedef struct Ctlr Ctlr; +typedef struct I2C I2C; + +struct I2C { + uchar i2mod; + uchar rsv12a[3]; + uchar i2add; + uchar rsv12b[3]; + uchar i2brg; + uchar rsv12c[3]; + uchar i2com; + uchar rsv12d[3]; + uchar i2cer; + uchar rsv12e[3]; + uchar i2cmr; +}; + +enum { + /* i2c-specific BD flags */ + RxeOV= 1<<1, /* overrun */ + TxS= 1<<10, /* transmit start condition */ + TxeNAK= 1<<2, /* last transmitted byte not acknowledged */ + TxeUN= 1<<1, /* underflow */ + TxeCL= 1<<0, /* collision */ + TxERR= (TxeNAK|TxeUN|TxeCL), + + /* i2cmod */ + REVD= 1<<5, /* =1, LSB first */ + GCD= 1<<4, /* =1, general call address disabled */ + FLT= 1<<3, /* =0, not filtered; =1, filtered */ + PDIV= 3<<1, /* predivisor field */ + EN= 1<<0, /* enable */ + + /* i2com */ + STR= 1<<7, /* start transmit */ + I2CM= 1<<0, /* master */ + I2CS= 0<<0, /* slave */ + + /* i2cer */ + TXE = 1<<4, + BSY = 1<<2, + TXB = 1<<1, + RXB = 1<<0, + + /* port B bits */ + I2CSDA = IBIT(27), + I2CSCL = IBIT(26), + + Rbit = 1<<0, /* bit in address byte denoting read */ + + /* maximum I2C I/O (can change) */ + MaxIO = 128, + MaxSA = 2, /* longest subaddress */ + Bufsize = (MaxIO+MaxSA+1+4)&~3, /* extra space for subaddress/clock bytes and alignment */ + Freq = 100000, + I2CTimeout = 250, /* msec */ + + Chatty = 0, +}; + +#define DPRINT if(Chatty)print + +/* data cache needn't be flushed if buffers allocated in uncached PHYSIMM */ +#define DCFLUSH(a,n) + +/* + * I2C software structures + */ + +struct Ctlr { + Lock; + QLock io; + int init; + int busywait; /* running before system set up */ + I2C* i2c; + IOCparam* sp; + + BD* rd; + BD* td; + int phase; + Rendez r; + char* addr; + char* txbuf; + char* rxbuf; +}; + +static Ctlr i2ctlr[1]; + +static void interrupt(Ureg*, void*); + +static void +enable(void) +{ + I2C *i2c; + + i2c = i2ctlr->i2c; + i2c->i2cer = ~0; /* clear events */ + eieio(); + i2c->i2mod |= EN; + eieio(); + i2c->i2cmr = TXE|BSY|TXB|RXB; /* enable all interrupts */ + eieio(); +} + +static void +disable(void) +{ + I2C *i2c; + + i2c = i2ctlr->i2c; + i2c->i2cmr = 0; /* mask all interrupts */ + i2c->i2mod &= ~EN; +} + +/* + * called by the reset routine of any driver using the I2C + */ +void +i2csetup(int busywait) +{ + IMM *io; + I2C *i2c; + IOCparam *sp; + CPMdev *cpm; + Ctlr *ctlr; + long f, e, emin; + int p, d, dmax; + + ctlr = i2ctlr; + ctlr->busywait = busywait; + if(ctlr->init) + return; + print("i2c setup...\n"); + ctlr->init = 1; + cpm = cpmdev(CPi2c); + i2c = cpm->regs; + ctlr->i2c = i2c; + sp = cpm->param; + if(sp == nil) + panic("I2C: can't allocate new parameter memory\n"); + ctlr->sp = sp; + disable(); + + if(ctlr->txbuf == nil){ + ctlr->txbuf = cpmalloc(Bufsize, 2); + ctlr->addr = ctlr->txbuf+MaxIO; + } + if(ctlr->rxbuf == nil) + ctlr->rxbuf = cpmalloc(Bufsize, 2); + if(ctlr->rd == nil){ + ctlr->rd = bdalloc(1); + ctlr->rd->addr = PADDR(ctlr->rxbuf); + ctlr->rd->length = 0; + ctlr->rd->status = BDWrap; + } + if(ctlr->td == nil){ + ctlr->td = bdalloc(2); + ctlr->td->addr = PADDR(ctlr->txbuf); + ctlr->td->length = 0; + ctlr->td->status = BDWrap|BDLast; + } + + /* select port pins */ + io = ioplock(); + io->pbdir |= I2CSDA | I2CSCL; + io->pbodr |= I2CSDA | I2CSCL; + io->pbpar |= I2CSDA | I2CSCL; + iopunlock(); + + /* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */ + sp = ctlr->sp; + sp->rbase = PADDR(ctlr->rd); + sp->tbase = PADDR(ctlr->td); + sp->rfcr = 0x18; + sp->tfcr = 0x18; + sp->mrblr = Bufsize; + sp->rstate = 0; + sp->rptr = 0; + sp->rbptr = sp->rbase; + sp->rcnt = 0; + sp->tstate = 0; + sp->tbptr = sp->tbase; + sp->tptr = 0; + sp->tcnt = 0; + eieio(); + + i2c->i2com = I2CM; + i2c->i2mod = 0; /* normal mode */ + i2c->i2add = 0; + + emin = Freq; + dmax = (m->cpuhz/Freq)/2-3; + for(d=0; d < dmax; d++){ + for(p=3; p>=0; p--){ + f = (m->cpuhz>>(p+2))/(2*(d+3)); + e = Freq - f; + if(e < 0) + e = -e; + if(e < emin){ + emin = e; + i2c->i2brg = d; + i2c->i2mod = (i2c->i2mod&~PDIV)|((3-p)<<1); /* set PDIV */ + } + } + } + //print("i2brg=%d i2mod=#%2.2ux\n", i2c->i2brg, i2c->i2mod); + intrenable(VectorCPIC+cpm->irq, interrupt, i2ctlr, BUSUNKNOWN, "i2c"); +} + +enum { + Idling, + Done, + Busy, + Sending, + Recving, +}; + +static void +interrupt(Ureg*, void *arg) +{ + int events; + Ctlr *ctlr; + I2C *i2c; + + ctlr = arg; + i2c = ctlr->i2c; + events = i2c->i2cer; + eieio(); + i2c->i2cer = events; + if(events & (BSY|TXE)){ + //print("I2C#%x\n", events); + if(ctlr->phase != Idling){ + ctlr->phase = Idling; + wakeup(&ctlr->r); + } + }else{ + if(events & TXB){ + //print("i2c: xmt %d %4.4ux %4.4ux\n", ctlr->phase, ctlr->td->status, ctlr->td[1].status); + if(ctlr->phase == Sending){ + ctlr->phase = Done; + wakeup(&ctlr->r); + } + } + if(events & RXB){ + //print("i2c: rcv %d %4.4ux %d\n", ctlr->phase, ctlr->rd->status, ctlr->rd->length); + if(ctlr->phase == Recving){ + ctlr->phase = Done; + wakeup(&ctlr->r); + } + } + } +} + +static int +done(void *a) +{ + return ((Ctlr*)a)->phase < Busy; +} + +static void +i2cwait(Ctlr *ctlr) +{ + int i; + + if(up == nil || ctlr->busywait){ + for(i=0; i < 5 && !done(ctlr); i++){ + delay(2); + interrupt(nil, ctlr); + } + }else + tsleep(&ctlr->r, done, ctlr, I2CTimeout); +} + +static int +i2cerror(char *s) +{ + if(up) + error(s); + /* no current process, don't call error */ + DPRINT("i2c error: %s\n", s); + return -1; +} + +long +i2csend(I2Cdev *d, void *buf, long n, ulong offset) +{ + Ctlr *ctlr; + int i, p, s; + + ctlr = i2ctlr; + if(up){ + if(n > MaxIO) + error(Etoobig); + qlock(&ctlr->io); + if(waserror()){ + qunlock(&ctlr->io); + nexterror(); + } + } + ctlr->txbuf[0] = d->addr<<1; + i = 1; + if(d->salen > 1) + ctlr->txbuf[i++] = offset>>8; + if(d->salen) + ctlr->txbuf[i++] = offset; + memmove(ctlr->txbuf+i, buf, n); + if(Chatty){ + print("tx: %8.8lux: ", PADDR(ctlr->txbuf)); + for(s=0; s<n+i; s++) + print(" %.2ux", ctlr->txbuf[s]&0xFF); + print("\n"); + } + DCFLUSH(ctlr->txbuf, Bufsize); + ilock(ctlr); + ctlr->phase = Sending; + ctlr->rd->status = BDEmpty|BDWrap|BDInt; + ctlr->td->addr = PADDR(ctlr->txbuf); + ctlr->td->length = n+i; + ctlr->td->status = BDReady|BDWrap|BDLast|BDInt; + enable(); + ctlr->i2c->i2com = STR|I2CM; + eieio(); + iunlock(ctlr); + i2cwait(ctlr); + disable(); + p = ctlr->phase; + s = ctlr->td->status; + if(up){ + poperror(); + qunlock(&ctlr->io); + } + if(s & BDReady) + return i2cerror("timed out"); + if(s & TxERR){ + sprint(up->genbuf, "write error: status %.4ux", s); + return i2cerror(up->genbuf); + } + if(p != Done) + return i2cerror("phase error"); + return n; +} + +long +i2crecv(I2Cdev *d, void *buf, long n, ulong offset) +{ + Ctlr *ctlr; + int p, s, flag, i; + BD *td; + long nr; + + ctlr = i2ctlr; + if(up){ + if(n > MaxIO) + error(Etoobig); + qlock(&ctlr->io); + if(waserror()){ + qunlock(&ctlr->io); + nexterror(); + } + } + ctlr->txbuf[0] = (d->addr<<1)|Rbit; + if(d->salen){ /* special write to set address */ + ctlr->addr[0] = d->addr<<1; + i = 1; + if(d->salen > 1) + ctlr->addr[i++] = offset >> 8; + ctlr->addr[i] = offset; + } + DCFLUSH(ctlr->txbuf, Bufsize); + DCFLUSH(ctlr->rxbuf, Bufsize); + ilock(ctlr); + ctlr->phase = Recving; + ctlr->rd->addr = PADDR(ctlr->rxbuf); + ctlr->rd->status = BDEmpty|BDWrap|BDInt; + flag = 0; + td = ctlr->td; + td[1].status = 0; + if(d->salen){ + /* special select sequence */ + td->addr = PADDR(ctlr->addr); + i = d->salen+1; + if(i > 3) + i = 3; + td->length = i; + /* td->status made BDReady below */ + td++; + flag = TxS; + } + td->addr = PADDR(ctlr->txbuf); + td->length = n+1; + td->status = BDReady|BDWrap|BDLast | flag; /* not BDInt: leave that to receive */ + if(flag) + ctlr->td->status = BDReady; + enable(); + ctlr->i2c->i2com = STR|I2CM; + eieio(); + iunlock(ctlr); + i2cwait(ctlr); + disable(); + p = ctlr->phase; + s = ctlr->td->status; + if(flag) + s |= ctlr->td[1].status; + nr = ctlr->rd->length; + if(up){ + poperror(); + qunlock(&ctlr->io); + } + DPRINT("nr=%ld %4.4ux %8.8lux\n", nr, ctlr->rd->status, ctlr->rd->addr); + if(nr > n) + nr = n; /* shouldn't happen */ + if(s & TxERR){ + sprint(up->genbuf, "read: tx status: %.4ux", s); + return i2cerror(up->genbuf); + } + if(s & BDReady || ctlr->rd->status & BDEmpty) + return i2cerror("timed out"); + if(p != Done) + return i2cerror("phase error"); + memmove(buf, ctlr->rxbuf, nr); + if(Chatty){ + for(s=0; s<nr; s++) + print(" %2.2ux", ctlr->rxbuf[s]&0xFF); + print("\n"); + } + return nr; +} diff --git a/os/mpc/i2c_spi.srx b/os/mpc/i2c_spi.srx new file mode 100644 index 00000000..6587549d --- /dev/null +++ b/os/mpc/i2c_spi.srx @@ -0,0 +1,149 @@ +S00600004844521B +S309022020007FFFEFD96E +S309022020043FFD000074 +S309022020087FFB49F7F2 +S3090220200C7FF9000030 +S309022020105FEFADF7B2 +S309022020145F89ADF714 +S309022020185FEFAFF7A8 +S3090220201C5F89AFF70A +S309022020203A9CFBC8FB +S30902202024E7C0EDF00C +S3090220202877C1E1BBB8 +S3090220202CF4DC7F1D1C +S30902202030ABAD932F6A +S309022020344E08FDCF5E +S309022020386E0FAFF858 +S3090220203C7CCF76CFE8 +S30902202040FD1FF9CF90 +S30902202044ABF88DC67A +S30902202048AB5679F7FB +S3090220204CB09373832F +S30902202050DFCE79F747 +S30902202054B091E6BB7E +S30902202058E5BBE74F86 +S3090220205CB3FA6F0F2D +S309022020606FFB76CEA6 +S30902202064EE0DF9CF8D +S309022020682BFBEFEF48 +S3090220206CCFEEF9CFC3 +S3090220207076CEAD242F +S3090220207490B2DF9A85 +S309022020787FDDD0BF51 +S3090220207C4BF847FDB1 +S309022020807CCF76CEA5 +S30902202084CFEF7E1FD5 +S309022020887F1D7DFD16 +S3090220208CF0B6EF7122 +S309022020907FC177C1AC +S30902202094FBC8607984 +S30902202098E722FBC850 +S3090220209C5FFFDFFFDC +S309022020A05FB2FFFB09 +S309022020A4FBC8F3C892 +S309022020A894A67F0152 +S309022020AC7F1D5F39D4 +S309022020B0AFE85F5EB0 +S309022020B4FFDFDF96AD +S309022020B8CB9FAF7D66 +S309022020BC5FC1AFED3C +S309022020C08C1C5FC12C +S309022020C4AFDD5FC342 +S309022020C8DF9A7EFDF8 +S309022020CCB0B25FB275 +S309022020D0FFFEABAD8F +S309022020D45FB2FFFED2 +S309022020D85FCE600B44 +S309022020DCE6BB600BCC +S309022020E05FCEDFC602 +S309022020E427FBEFDFE0 +S309022020E85FC8CFDEF8 +S309022020EC3A9CE7C04B +S309022020F0EDF0F3C82C +S309022020F47F0154CD1F +S309022020F87F1D2D3DB6 +S309022020FC363A757063 +S309022021007E0AF1CE6C +S3090220210437EF2E68F3 +S309022021087FEE10EC42 +S3090220210CADF8EFDE35 +S30902202110CFEAE52FD6 +S309022021147D0FE12B07 +S30902202118F1CE5F6518 +S3090220211C7E0A4DF8CA +S30902202120CFEA5F7209 +S309022021247D0BEFEE2A +S30902202128CFEA5F74FF +S3090220212CE522EFDEB3 +S309022021305F74CFDA07 +S309022021340B6273851A +S30902202138DF627E0AB2 +S3090220213C30D8145B00 +S30902202140BFFFF3C8FA +S309022021445FFFDFFF33 +S30902202148A7F85F5E0F +S3090220214CBFFE7F7DAE +S3090220215010D314501C +S309022021545F36BFFF0C +S30902202158AF785F5E77 +S3090220215CBFFDA7F8FC +S309022021605F36BFFE01 +S3090220216477FD30C0EB +S309022021684E08FDCF29 +S3090220216CE5FF6E0FE6 +S30902202170AFF87E1FFF +S309022021747E0FFD1F96 +S30902202178F1CF5F1B01 +S3090220217CABF80D5E29 +S309022021805F5EFFEF88 +S3090220218479F730A2ED +S30902202188AFDD5F340C +S3090220218C47F85F3455 +S30902202190AFED7FDD2B +S3090220219450B249785C +S3090220219847FD7F1D3B +S3090220219C7DFD70AD80 +S309022021A0EF717EC174 +S309022021A46BA47F0180 +S309022021A82D267EFD3D +S309022021AC30DE5F5E3C +S309022021B0FFFD5F5E4A +S309022021B4FFEF5F5E54 +S309022021B8FFDF0CA071 +S309022021BCAFED0A9EB3 +S309022021C0AFDD0C3A21 +S309022021C45F3AAFBDEA +S309022021C87FBDB0827D +S309022021CC5F8247F8C7 +S9030000FC +S00600004844521B +S30902202F003E303430D3 +S30902202F0434343737CB +S30902202F08ABF7BF9BA1 +S30902202F0C994B4FBDA9 +S30902202F10BD59949358 +S30902202F14349FFF3788 +S30902202F18FB9B177D63 +S30902202F1CD99369565E +S30902202F20BBFDD69760 +S30902202F24BDD2FD11E4 +S30902202F2831DB9BB323 +S30902202F2C6313963736 +S30902202F3093733693A6 +S30902202F34193137F7F9 +S30902202F38331737AF3D +S30902202F3C7BB9B999E3 +S30902202F40BB197957C1 +S30902202F447FDFD3D55B +S30902202F4873B773F7C9 +S30902202F4C37933B99BB +S30902202F501D115316BE +S30902202F54993153151F +S30902202F5831694BF474 +S30902202F5CFBDBD35947 +S30902202F603149735305 +S30902202F6476956D6960 +S30902202F687B9D9693FC +S30902202F6C1313197981 +S30902202F7079376935E7 +S9030000FC
\ No newline at end of file diff --git a/os/mpc/inb.s b/os/mpc/inb.s new file mode 100644 index 00000000..4dcd7fe4 --- /dev/null +++ b/os/mpc/inb.s @@ -0,0 +1,120 @@ +#include "mem.h" + +#define BDNZ BC 16,0, + +TEXT inb(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVBZ (R3), R3 + RETURN + +TEXT insb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $1, R4 +insb1: + EIEIO + MOVBZ (R3), R7 + MOVBU R7, 1(R4) + BDNZ insb1 + RETURN + +TEXT outb(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVB R4, (R3) + RETURN + +TEXT outsb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $1, R4 +outsb1: + EIEIO + MOVBZU 1(R4), R7 + MOVB R7, (R3) + BDNZ outsb1 + RETURN + +TEXT ins(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVHBR (R3), R3 + RETURN + +TEXT inss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $2, R4 +inss1: + EIEIO + MOVHZ (R3), R7 + MOVHU R7, 2(R4) + BDNZ inss1 + RETURN + +TEXT outs(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVHBR R4, (R3) + RETURN + +TEXT outss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $2, R4 +outss1: + EIEIO + MOVHZU 2(R4), R7 + MOVH R7, (R3) + BDNZ outss1 + RETURN + +TEXT inl(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVWBR (R3), R3 + RETURN + +TEXT insl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $4, R4 +insl1: + EIEIO + MOVW (R3), R7 + MOVWU R7, 4(R4) + BDNZ insl1 + RETURN + +TEXT outl(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVWBR R4, (R3) + RETURN + +TEXT outsl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $4, R4 +outsl1: + EIEIO + MOVWU 4(R4), R7 + MOVW R7, (R3) + BDNZ outsl1 + RETURN diff --git a/os/mpc/kbd.c b/os/mpc/kbd.c new file mode 100644 index 00000000..81e2384c --- /dev/null +++ b/os/mpc/kbd.c @@ -0,0 +1,20 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * initialise the keyboard Queue if uartinstall hasn't already done so + */ +void +kbdinit(void) +{ + if(kbdq == nil){ + kbdq = qopen(4*1024, 0, 0, 0); + qnoblock(kbdq, 1); + } + archkbdinit(); +} diff --git a/os/mpc/l.s b/os/mpc/l.s new file mode 100644 index 00000000..8a2ae66d --- /dev/null +++ b/os/mpc/l.s @@ -0,0 +1,685 @@ +#include "mem.h" + +#define MB (1024*1024) + +/* + * options + */ +#undef MMUTWC /* we don't map enough memory to need table walk */ +#undef SHOWCYCLE /* might be needed for BDM debugger to keep control */ + +/* + * common ppc special purpose registers + */ +#define DSISR 18 +#define DAR 19 /* Data Address Register */ +#define DEC 22 /* Decrementer */ +#define SRR0 26 /* Saved Registers (exception) */ +#define SRR1 27 +#define SPRG0 272 /* Supervisor Private Registers */ +#define SPRG1 273 +#define SPRG2 274 +#define SPRG3 275 +#define TBRU 269 /* Time base Upper/Lower (Reading) */ +#define TBRL 268 +#define TBWU 285 /* Time base Upper/Lower (Writing) */ +#define TBWL 284 +#define PVR 287 /* Processor Version */ + +/* + * mpc8xx-specific special purpose registers of interest here + */ +#define EIE 80 +#define EID 81 +#define NRI 82 +#define IMMR 638 +#define IC_CSR 560 +#define IC_ADR 561 +#define IC_DAT 562 +#define DC_CSR 568 +#define DC_ADR 569 +#define DC_DAT 570 +#define MI_CTR 784 +#define MI_AP 786 +#define MI_EPN 787 +#define MI_TWC 789 +#define MI_RPN 790 +#define MI_DBCAM 816 +#define MI_DBRAM0 817 +#define MI_DBRAM1 818 +#define MD_CTR 792 +#define M_CASID 793 +#define MD_AP 794 +#define MD_EPN 795 +#define M_TWB 796 +#define MD_TWC 797 +#define MD_RPN 798 +#define M_TW 799 +#define MD_DBCAM 824 +#define MD_DBRAM0 825 +#define MD_DBRAM1 826 + +/* use of SPRG registers in save/restore */ +#define SAVER0 SPRG0 +#define SAVER1 SPRG1 +#define SAVELR SPRG2 +#define SAVEXX SPRG3 + +/* special instruction definitions */ +#define BDNZ BC 16,0, +#define BDNE BC 0,2, +#define TLBIA WORD $((31<<26)|(370<<1)) +#define MFTB(tbr,d) WORD $((31<<26)|((d)<<21)|((tbr&0x1f)<<16)|(((tbr>>5)&0x1f)<<11)|(371<<1)) + +/* on some models mtmsr doesn't synchronise enough (eg, 603e) */ +#define MSRSYNC SYNC; ISYNC + +#define UREGSPACE (UREGSIZE+8) + +/* could define STEP to set an LED to mark progress */ +#define STEP(x) + +/* + * Boot first processor + */ + TEXT start(SB), $-4 + + MOVW MSR, R3 + RLWNM $0, R3, $~EE, R3 + RLWNM $0, R3, $~FPE, R3 + OR $ME, R3 + ISYNC + MOVW R3, MSR /* turn off interrupts but enable traps */ + MSRSYNC + MOVW $0, R0 /* except during trap handling, R0 is zero from now on */ + MOVW R0, CR + MOVW $setSB(SB), R2 + +/* + * reset the caches and disable them for now + */ + MOVW SPR(IC_CSR), R4 /* read and clear */ + MOVW $(5<<25), R4 + MOVW R4, SPR(IC_CSR) /* unlock all */ + ISYNC + MOVW $(6<<25), R4 + MOVW R4, SPR(IC_CSR) /* invalidate all */ + ISYNC + MOVW $(2<<25), R4 + MOVW R4, SPR(IC_CSR) /* disable i-cache */ + ISYNC + + SYNC + MOVW SPR(DC_CSR), R4 /* read and clear */ + MOVW $(10<<24), R4 + SYNC + MOVW R4, SPR(DC_CSR) /* unlock all */ + ISYNC + MOVW $(12<<24), R4 + SYNC + MOVW R4, SPR(DC_CSR) /* invalidate all */ + ISYNC + MOVW $(4<<24), R4 + SYNC + MOVW R4, SPR(DC_CSR) /* disable d-cache */ + ISYNC + +#ifdef SHOWCYCLE + MOVW $0, R4 +#else + MOVW $7, R4 +#endif + MOVW R4, SPR(158) /* cancel `show cycle' for normal instruction execution */ + ISYNC + +/* + * set other system configuration values + */ + MOVW $PHYSIMM, R4 + MOVW R4, SPR(IMMR) /* set internal memory base */ + +STEP(1) + + BL kernelmmu(SB) + +STEP(2) + /* no kfpinit on 82x */ + + MOVW $mach0(SB), R(MACH) + ADD $(MACHSIZE-8), R(MACH), R1 + SUB $4, R(MACH), R3 + ADD $4, R1, R4 +clrmach: + MOVWU R0, 4(R3) + CMP R3, R4 + BNE clrmach + + MOVW R0, R(USER) + MOVW R0, 0(R(MACH)) + + MOVW $edata(SB), R3 + MOVW $end(SB), R4 + ADD $4, R4 + SUB $4, R3 +clrbss: + MOVWU R0, 4(R3) + CMP R3, R4 + BNE clrbss + +STEP(3) + BL main(SB) + BR 0(PC) + +TEXT kernelmmu(SB), $0 + TLBIA + ISYNC + + MOVW $0, R4 + MOVW R4, SPR(M_CASID) /* set supervisor space */ + MOVW $(0<<29), R4 /* allow i-cache when IR=0 */ + MOVW R4, SPR(MI_CTR) /* i-mmu control */ + ISYNC + MOVW $((1<<29)|(1<<28)), R4 /* cache inhibit when DR=0, write-through */ + SYNC + MOVW R4, SPR(MD_CTR) /* d-mmu control */ + ISYNC + TLBIA + + /* map various things 1:1 */ + MOVW $tlbtab-KZERO(SB), R4 + MOVW $tlbtabe-KZERO(SB), R5 + SUB R4, R5 + MOVW $(3*4), R6 + DIVW R6, R5 + SUB $4, R4 + MOVW R5, CTR +ltlb: + MOVWU 4(R4), R5 + MOVW R5, SPR(MD_EPN) + MOVW R5, SPR(MI_EPN) + MOVWU 4(R4), R5 + MOVW R5, SPR(MI_TWC) + MOVW R5, SPR(MD_TWC) + MOVWU 4(R4), R5 + MOVW R5, SPR(MD_RPN) + MOVW R5, SPR(MI_RPN) + BDNZ ltlb + + MOVW $(1<<25), R4 + MOVW R4, SPR(IC_CSR) /* enable i-cache */ + ISYNC + + MOVW $(3<<24), R4 + SYNC + MOVW R4, SPR(DC_CSR) /* clear force write through mode */ + MOVW $(2<<24), R4 + SYNC + MOVW R4, SPR(DC_CSR) /* enable d-cache */ + ISYNC + + /* enable MMU and set kernel PC to virtual space */ + MOVW $((0<<29)|(0<<28)), R4 /* cache when DR=0, write back */ + SYNC + MOVW R4, SPR(MD_CTR) /* d-mmu control */ + MOVW LR, R3 + OR $KZERO, R3 + MOVW R3, SPR(SRR0) + MOVW MSR, R4 + OR $(ME|IR|DR), R4 /* had ME|FPE|FE0|FE1 */ + MOVW R4, SPR(SRR1) + RFI /* resume in kernel mode in caller */ + +TEXT splhi(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $~EE, R4 + SYNC + MOVW R4, MSR + MSRSYNC + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + RETURN + +TEXT splx(SB), $0 + MOVW MSR, R4 + RLWMI $0, R3, $EE, R4 + RLWNMCC $0, R3, $EE, R5 + BNE splx0 + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ +splx0: + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT splxpc(SB), $0 + MOVW MSR, R4 + RLWMI $0, R3, $EE, R4 + RLWNMCC $0, R3, $EE, R5 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spllo(SB), $0 + MFTB(TBRL, 3) + MOVW R3, spltbl(SB) + MOVW MSR, R3 + OR $EE, R3, R4 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spldone(SB), $0 + RETURN + +TEXT islo(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $EE, R3 + RETURN + +TEXT setlabel(SB), $-4 + MOVW LR, R31 + MOVW R1, 0(R3) + MOVW R31, 4(R3) + MOVW $0, R3 + RETURN + +TEXT gotolabel(SB), $-4 + MOVW 4(R3), R31 + MOVW R31, LR + MOVW 0(R3), R1 + MOVW $1, R3 + RETURN + +/* + * enter with stack set and mapped. + * on return, SB (R2) has been set, and R3 has the Ureg*, + * the MMU has been re-enabled, kernel text and PC are in KSEG, + * R(MACH) has been set, and R0 contains 0. + * + * this can be simplified in the Inferno regime + */ +TEXT saveureg(SB), $-4 +/* + * save state + */ + MOVMW R2, 48(R1) /* r2:r31 */ + MOVW $setSB(SB), R2 + MOVW SPR(SAVER1), R4 + MOVW R4, 44(R1) + MOVW SPR(SAVER0), R5 + MOVW R5, 40(R1) + MOVW CTR, R6 + MOVW R6, 36(R1) + MOVW XER, R4 + MOVW R4, 32(R1) + MOVW CR, R5 + MOVW R5, 28(R1) + MOVW SPR(SAVELR), R6 /* LR */ + MOVW R6, 24(R1) + /* pad at 20(R1) */ + /* old PC(16) and status(12) saved earlier */ + MOVW SPR(SAVEXX), R0 + MOVW R0, 8(R1) /* cause/vector */ + ADD $8, R1, R3 /* Ureg* */ + STWCCC R3, (R1) /* break any pending reservations */ + MOVW $0, R0 /* compiler/linker expect R0 to be zero */ + + MOVW MSR, R5 + OR $(IR|DR), R5 /* enable MMU */ + MOVW R5, SPR(SRR1) + MOVW LR, R31 + OR $KZERO, R31 /* return PC in KSEG0 */ + MOVW R31, SPR(SRR0) + SYNC + ISYNC + RFI /* returns to trap handler */ + +TEXT icflush(SB), $-4 /* icflush(virtaddr, count) */ + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +icf0: ICBI (R5) + ADD $CACHELINESZ, R5 + BDNZ icf0 + ISYNC + RETURN + +/* + * flush to store and invalidate globally + */ +TEXT dcflush(SB), $-4 /* dcflush(virtaddr, count) */ + SYNC + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE dcf1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +dcf0: DCBF (R5) + ADD $CACHELINESZ, R5 + BDNZ dcf0 + SYNC + ISYNC +dcf1: + RETURN + +/* + * invalidate without flush, globally + */ +TEXT dcinval(SB), $-4 /* dcinval(virtaddr, count) */ + SYNC + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE dci1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +dci0: DCBI (R5) + ADD $CACHELINESZ, R5 + BDNZ dci0 + SYNC + ISYNC +dci1: + RETURN + +TEXT _tas(SB), $0 + SYNC + MOVW R3, R4 + MOVW $0xdeaddead,R5 +tas1: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + CMP R3, $0 + BNE tas0 + STWCCC R5, (R4) + BNE tas1 +tas0: + SYNC + ISYNC + RETURN + +TEXT gettbl(SB), $0 + MFTB(TBRL, 3) + RETURN + +TEXT gettbu(SB), $0 + MFTB(TBRU, 3) + RETURN + +TEXT getpvr(SB), $0 + MOVW SPR(PVR), R3 + RETURN + +TEXT getimmr(SB), $0 + MOVW SPR(IMMR), R3 + RETURN + +TEXT getdec(SB), $0 + MOVW SPR(DEC), R3 + RETURN + +TEXT putdec(SB), $0 + MOVW R3, SPR(DEC) + RETURN + +TEXT getcallerpc(SB), $-4 + MOVW 0(R1), R3 + RETURN + +TEXT getdar(SB), $0 + MOVW SPR(DAR), R3 + RETURN + +TEXT getdsisr(SB), $0 + MOVW SPR(DSISR), R3 + RETURN + +TEXT getdepn(SB), $0 + MOVW SPR(MD_EPN), R3 + RETURN + +TEXT getmsr(SB), $0 + MOVW MSR, R3 + RETURN + +TEXT putmsr(SB), $0 + SYNC + MOVW R3, MSR + MSRSYNC + RETURN + +TEXT eieio(SB), $0 + EIEIO + RETURN + +TEXT gotopc(SB), $0 + MOVW R3, CTR + MOVW LR, R31 /* for trace back */ + BR (CTR) + +TEXT firmware(SB), $0 + MOVW MSR, R3 + MOVW $(EE|ME), R4 + ANDN R4, R3 + OR $(MSR_IP), R3 + ISYNC + MOVW R3, MSR /* turn off interrupts and machine checks */ + MSRSYNC + MOVW $(RI|IR|DR|ME), R4 + ANDN R4, R3 + MOVW R3, SPR(SRR1) + MOVW $(0xFF00<<16), R4 + MOVW R4, SPR(IMMR) + MOVW $(0x0800<<16), R4 + MOVW R4, SPR(SRR0) /* force bad address */ + MOVW R0, SPR(149) /* ensure checkstop on machine check */ + MOVW R4, R1 + MOVW R4, R2 + EIEIO + ISYNC + RFI + +/* + * byte swapping of arrays of long and short; + * could possibly be avoided with more changes to drivers + */ +TEXT swabl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + SRAW $2, R5, R5 + MOVW R5, CTR + SUB $4, R4 + SUB $4, R3 +swabl1: + ADD $4, R3 + MOVWU 4(R4), R7 + MOVWBR R7, (R3) + BDNZ swabl1 + RETURN + +TEXT swabs(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + SRAW $1, R5, R5 + MOVW R5, CTR + SUB $2, R4 + SUB $2, R3 +swabs1: + ADD $2, R3 + MOVHZU 2(R4), R7 + MOVHBR R7, (R3) + BDNZ swabs1 + RETURN + +TEXT legetl(SB), $0 + MOVWBR (R3), R3 + RETURN + +TEXT lesetl(SB), $0 + MOVW v+4(FP), R4 + MOVWBR R4, (R3) + RETURN + +TEXT legets(SB), $0 + MOVHBR (R3), R3 + RETURN + +TEXT lesets(SB), $0 + MOVW v+4(FP), R4 + MOVHBR R4, (R3) + RETURN + +#ifdef MMUTWC +/* + * ITLB miss + * avoid references that might need the right SB value; + * IR and DR are off. + */ +TEXT itlbmiss(SB), $-4 + MOVW R1, SPR(M_TW) + MOVW SPR(SRR0), R1 /* instruction miss address */ + MOVW R1, SPR(MD_EPN) + MOVW SPR(M_TWB), R1 /* level one pointer */ + MOVW (R1), R1 + MOVW R1, SPR(MI_TWC) /* save level one attributes */ + MOVW R1, SPR(MD_TWC) /* save base and attributes */ + MOVW SPR(MD_TWC), R1 /* level two pointer */ + MOVW (R1), R1 /* level two entry */ + MOVW R1, SPR(MI_RPN) /* write TLB */ + MOVW SPR(M_TW), R1 + RFI + +/* + * DTLB miss + * avoid references that might need the right SB value; + * IR and DR are off. + */ +TEXT dtlbmiss(SB), $-4 + MOVW R1, SPR(M_TW) + MOVW SPR(M_TWB), R1 /* level one pointer */ + MOVW (R1), R1 /* level one entry */ + MOVW R1, SPR(MD_TWC) /* save base and attributes */ + MOVW SPR(MD_TWC), R1 /* level two pointer */ + MOVW (R1), R1 /* level two entry */ + MOVW R1, SPR(MD_RPN) /* write TLB */ + MOVW SPR(M_TW), R1 + RFI +#else +TEXT itlbmiss(SB), $-4 + BR traps +TEXT dtlbmiss(SB), $-4 + BR traps +#endif + +/* + * traps force memory mapping off. + * this code goes to too much effort (for the Inferno environment) to restore it. + */ +TEXT trapvec(SB), $-4 +traps: + MOVW LR, R0 + +pagefault: + +/* + * map data virtually and make space to save + */ + MOVW R0, SPR(SAVEXX) /* vector */ + MOVW R1, SPR(SAVER1) + SYNC + ISYNC + MOVW MSR, R0 + OR $(DR|ME), R0 /* make data space usable */ + SYNC + MOVW R0, MSR + MSRSYNC + SUB $UREGSPACE, R1 + + MOVW SPR(SRR0), R0 /* save SRR0/SRR1 now, since DLTB might be missing stack page */ + MOVW R0, LR + MOVW SPR(SRR1), R0 + MOVW R0, 12(R1) /* save status: could take DLTB miss here */ + MOVW LR, R0 + MOVW R0, 16(R1) /* old PC */ + BL saveureg(SB) + BL trap(SB) + BR restoreureg + +TEXT intrvec(SB), $-4 + MOVW LR, R0 + +/* + * map data virtually and make space to save + */ + MOVW R0, SPR(SAVEXX) /* vector */ + MOVW R1, SPR(SAVER1) + SYNC + ISYNC + MOVW MSR, R0 + OR $DR, R0 /* make data space usable */ + SYNC + MOVW R0, MSR + MSRSYNC + SUB $UREGSPACE, R1 + + MFTB(TBRL, 0) + MOVW R0, intrtbl(SB) + + MOVW SPR(SRR0), R0 + MOVW R0, LR + MOVW SPR(SRR1), R0 + MOVW R0, 12(R1) + MOVW LR, R0 + MOVW R0, 16(R1) + BL saveureg(SB) + + MFTB(TBRL, 5) + MOVW R5, isavetbl(SB) + + BL intr(SB) + +/* + * restore state from Ureg and return from trap/interrupt + */ +restoreureg: + MOVMW 48(R1), R2 /* r2:r31 */ + /* defer R1 */ + MOVW 40(R1), R0 + MOVW R0, SPR(SAVER0) + MOVW 36(R1), R0 + MOVW R0, CTR + MOVW 32(R1), R0 + MOVW R0, XER + MOVW 28(R1), R0 + MOVW R0, CR /* CR */ + MOVW 24(R1), R0 + MOVW R0, SPR(SAVELR) /* LR */ + /* pad, skip */ + MOVW 16(R1), R0 + MOVW R0, SPR(SRR0) /* old PC */ + MOVW 12(R1), R0 + MOVW R0, SPR(SRR1) /* old MSR */ + /* cause, skip */ + MOVW 44(R1), R1 /* old SP */ + MOVW SPR(SAVELR), R0 + MOVW R0, LR + MOVW SPR(SAVER0), R0 + RFI + +GLOBL mach0+0(SB), $MACHSIZE +GLOBL spltbl+0(SB), $4 +GLOBL intrtbl+0(SB), $4 +GLOBL isavetbl+0(SB), $4 diff --git a/os/mpc/nofp.s b/os/mpc/nofp.s new file mode 100644 index 00000000..f23d49b0 --- /dev/null +++ b/os/mpc/nofp.s @@ -0,0 +1,31 @@ +/* + * stubs when no floating-point hardware + */ + +TEXT kfpinit(SB), $0 + RETURN + +TEXT getfpscr(SB), $8 + MOVW $0, R3 + RETURN + +TEXT fpsave(SB), $0 + RETURN + +TEXT fprestore(SB), $0 + RETURN + +TEXT clrfptrap(SB), $0 + RETURN + +TEXT fpinit(SB), $0 + RETURN + +TEXT fpoff(SB), $0 + RETURN + +TEXT FPsave(SB), 1, $0 + RETURN + +TEXT FPrestore(SB), 1, $0 + RETURN diff --git a/os/mpc/pcmcia.h b/os/mpc/pcmcia.h new file mode 100644 index 00000000..2e22042f --- /dev/null +++ b/os/mpc/pcmcia.h @@ -0,0 +1,19 @@ +/* + * PCMCIA support code. + */ +int inb(int); +int inb(int); +ulong inl(int); +ushort ins(int); +void insb(int, void*, int); +void insl(int, void*, int); +void inss(int, void*, int); +void outb(int, int); +void outl(int, ulong); +void outs(int, ushort); +void outsb(int, void*, int); +void outsl(int, void*, int); +void outss(int, void*, int); +void pcmintrenable(int, void(*)(Ureg*,void*), void*); +int pcmspecial(char*, ISAConf*); +void pcmspecialclose(int); diff --git a/os/mpc/pit.c b/os/mpc/pit.c new file mode 100644 index 00000000..bfc8bc8d --- /dev/null +++ b/os/mpc/pit.c @@ -0,0 +1,68 @@ +/* + * programmable interrupt timer + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +enum { + /* piscr */ + PTE = 1<<0, + PITF = 1<<1, + PIE = 1<<2, + PS = 1<<7, +}; + +static void +pitinterrupt(Ureg*, void*) +{ + IMM *io; + + io = m->iomem; + if(io->piscr & PS){ + io->piscr |= PS; /* clear by writing 1 */ + /* do whatever is required */ + } +} + +static void +pitreset(void) +{ + IMM *io; + + io = ioplock(); + io->piscrk = KEEP_ALIVE_KEY; + io->piscr = (PITlevel<<8) | PS | PITF; + if(0) + io->piscrk = ~KEEP_ALIVE_KEY; + /* piscrk is left unlocked for interrupt routine */ + iopunlock(); + intrenable(PITlevel, pitinterrupt, nil, BUSUNKNOWN, "pit"); +} + +static ulong +pitload(ulong usec) +{ + IMM *io; + ulong v; + + v = ((usec*m->oscclk)/512); + if(v == 0 || v >= (1<<16)) + return 0; /* can't do */ + io = ioplock(); + io->pitck = KEEP_ALIVE_KEY; + io->pitc = (v-1)<<16; + io->pitck = ~KEEP_ALIVE_KEY; + io->piscrk = KEEP_ALIVE_KEY; + io->piscr = (PITlevel<<8) | PS | PIE | PITF | PTE; + if(0) + io->piscrk = ~KEEP_ALIVE_KEY; + /* piscrk is left unlocked for interrupt routine */ + iopunlock(); + return (v*512)/m->oscclk; +} diff --git a/os/mpc/powerbreak.c b/os/mpc/powerbreak.c new file mode 100644 index 00000000..fc5970ef --- /dev/null +++ b/os/mpc/powerbreak.c @@ -0,0 +1,123 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "ureg.h" + +extern int (*breakhandler)(Ureg *ur, Proc*); /* trap.c */ +extern Instr BREAK; /* trap.c */ +extern void portbreakinit(void); + +#define getop(i) ((i>>26)&0x3F) +#define getxo(i) ((i>>1)&0x3FF) +#define getbobi(i) bo = (i>>21)&0x1f; bi = (i>>16)&0x1f; xx = (i>>11)&0x1f; + +void +machbreakinit(void) +{ + portbreakinit(); + breakhandler = breakhit; +} + +Instr +machinstr(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + return *(Instr*)addr; +} + +void +machbreakset(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = BREAK; + segflush((void*)addr, sizeof(Instr)); +} + +void +machbreakclear(ulong addr, Instr i) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = i; + segflush((void*)addr, sizeof(Instr)); +} + +static int +condok(Ureg *ur, ulong ir, int ctrok) +{ + int bo, bi, xx; + ulong ctrval; + + ctrval = ur->ctr; + getbobi(ir); + if(xx) + return 0; /* illegal */ + if((bo & 0x4) == 0) { + if(!ctrok) + return 0; /* illegal */ + ctrval--; + } + if(bo & 0x4 || (ctrval!=0)^((bo>>1)&1)) { + if(bo & 0x10 || (((ur->cr & (1L<<(31-bi))!=0)==((bo>>3)&1)))) + return 1; + } + return 0; +} + +/* + * Return the address of the instruction that will be executed after the + * instruction at ur->pc, accounting for current branch conditions. + */ +ulong +machnextaddr(Ureg *ur) +{ + long imm; + ulong ir; + + ir = *(ulong*)ur->pc; + switch(getop(ir)) { + case 18: /* branch */ + imm = ir & 0x03FFFFFC; + if(ir & 0x02000000) + imm |= 0xFC000000; /* sign extended */ + if((ir & 2) == 0) /* relative address */ + return ur->pc + imm; + return imm; + + case 16: /* conditional branch */ + if(condok(ur, ir&0xFFFF0000, 1)){ + imm = ir & 0xFFFC; + if(ir & 0x08000) + imm |= 0xFFFF0000; /* sign extended */ + if((ir & 2) == 0) /* relative address */ + return ur->pc + imm; + return imm; + } + break; + + case 19: /* conditional branch to register */ + switch(getxo(ir)){ + case 528: /* bcctr */ + if(condok(ur, ir, 0)) + return ur->ctr & ~3; + break; + case 16: /* bclr */ + if(condok(ur, ir, 1)) + return ur->lr & ~3; + break; + } + break; + } + return ur->pc+4; /* next instruction */ +} + +int +isvalid_va(void *v) +{ + return (ulong)v >= KTZERO; +} diff --git a/os/mpc/rmap.c b/os/mpc/rmap.c new file mode 100644 index 00000000..f521c24d --- /dev/null +++ b/os/mpc/rmap.c @@ -0,0 +1,106 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +mapinit(RMap *rmap, Map *map, int size) +{ + lock(rmap); + rmap->map = map; + rmap->mapend = map+(size/sizeof(Map)); + unlock(rmap); +} + +void +mapfree(RMap* rmap, ulong addr, int size) +{ + Map *mp; + ulong t; + + if(size <= 0) + return; + + lock(rmap); + for(mp = rmap->map; mp->addr <= addr && mp->size; mp++) + ; + + if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){ + (mp-1)->size += size; + if(addr+size == mp->addr){ + (mp-1)->size += mp->size; + while(mp->size){ + mp++; + (mp-1)->addr = mp->addr; + (mp-1)->size = mp->size; + } + } + } + else{ + if(addr+size == mp->addr && mp->size){ + mp->addr -= size; + mp->size += size; + } + else do{ + if(mp >= rmap->mapend){ + print("mapfree: %s: losing 0x%luX, %d\n", + rmap->name, addr, size); + break; + } + t = mp->addr; + mp->addr = addr; + addr = t; + t = mp->size; + mp->size = size; + mp++; + }while(size = t); + } + unlock(rmap); +} + +ulong +rmapalloc(RMap* rmap, ulong addr, int size, int align) +{ + Map *mp; + ulong maddr, oaddr; + + lock(rmap); + for(mp = rmap->map; mp->size; mp++){ + maddr = mp->addr; + + if(addr){ + if(maddr > addr) + break; + if(maddr+mp->size < addr) + continue; + if(addr+size > maddr+mp->size) + break; + maddr = addr; + } + + if(align > 0) + maddr = ((maddr+align-1)/align)*align; + if(mp->addr+mp->size-maddr < size) + continue; + + oaddr = mp->addr; + mp->addr = maddr+size; + mp->size -= maddr-oaddr+size; + if(mp->size == 0){ + do{ + mp++; + (mp-1)->addr = mp->addr; + }while((mp-1)->size = mp->size); + } + + unlock(rmap); + if(oaddr != maddr) + mapfree(rmap, oaddr, maddr-oaddr); + + return maddr; + } + unlock(rmap); + + return 0; +} diff --git a/os/mpc/screen.c b/os/mpc/screen.c new file mode 100644 index 00000000..59fff7f6 --- /dev/null +++ b/os/mpc/screen.c @@ -0,0 +1,832 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> + +#include "screen.h" + +enum { + Backgnd = 0xFF, /* white */ + Foregnd = 0x00, /* black */ +}; + +Cursor arrow = { + { -1, -1 }, + { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, + 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, + 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, + }, + { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, + 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, + 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, + 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, + }, +}; + +static Memdata xgdata; +static Memimage xgscreen = +{ + {0, 0, 0, 0}, /* r */ + {0, 0, 0, 0}, /* clipr */ + 8, /* depth */ + 1, /* nchan */ + CMAP8, /* chan */ + nil, /* cmap */ + &xgdata, /* data */ + 0, /* zero */ + 0, /* width */ + nil, /* layer */ + 0, /* flags */ +}; + +int novgascreen; /* optionally set by configuration file */ +static int lcdpdpar; /* value to load into io->pdpar */ + +Memimage *gscreen; +Memimage *conscol; +Memimage *back; + +static Memsubfont *memdefont; +static Lock palettelock; /* access to DAC registers */ +static Lock screenlock; +static int h; +static Point curpos; +static Rectangle window; + +typedef struct SWcursor SWcursor; +static SWcursor *swc = nil; +SWcursor* swcurs_create(ulong *, int, int, Rectangle, int); +void swcurs_destroy(SWcursor*); +void swcurs_enable(SWcursor*); +void swcurs_disable(SWcursor*); +void swcurs_hide(SWcursor*); +void swcurs_unhide(SWcursor*); +void swcurs_load(SWcursor*, Cursor*); + +static void screenputc(char*); +static void scroll(void); +static void setscreen(Mode*); +static void cursorlock(Rectangle); +static void cursorunlock(void); +static void lcdinit(Mode*); +static void lcdsetrgb(int, ulong, ulong, ulong); + +/* + * Called by main(). + */ +void +screeninit(void) +{ + Mode m; + +novgascreen=1; return; + + /* default size and parameters */ + memset(&m.lcd, 0, sizeof(m.lcd)); + m.x = 640; + m.y = 480; + m.d = 3; + if(novgascreen == 0 && archlcdmode(&m) >= 0){ + memdefont = getmemdefont(); + setscreen(&m); + } +} + +/* + * On 8 bit displays, load the default color map + */ +void +graphicscmap(int invert) +{ + int num, den, i, j; + int r, g, b, cr, cg, cb, v; + + for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){ + for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){ + den=r; + if(g>den) den=g; + if(b>den) den=b; + if(den==0) /* divide check -- pick grey shades */ + cr=cg=cb=v*17; + else{ + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + if(invert) + setcolor(255-i-(j&15), + cr*0x01010101, cg*0x01010101, cb*0x01010101); + else + setcolor(i+(j&15), + cr*0x01010101, cg*0x01010101, cb*0x01010101); + } + } +} + +/* + * reconfigure screen shape + */ +static void +setscreen(Mode *mode) +{ + int h; + + if(swc) + swcurs_destroy(swc); + + gscreen = &xgscreen; + xgdata.ref = 1; + lcdinit(mode); + xgdata.bdata = (uchar*)mode->aperture; + if(xgdata.bdata == nil) + panic("setscreen: vga soft memory"); + + gscreen->r = Rect(0, 0, mode->x, mode->y); + gscreen->clipr = gscreen->r; + gscreen->depth = 1<<mode->d; + gscreen->width = wordsperline(gscreen->r, gscreen->depth); + memimageinit(); + memdefont = getmemdefont(); + + memsetchan(gscreen, CMAP8); + back = memwhite; + conscol = memblack; + memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD); + graphicscmap(0); + + /* get size for a system window */ + h = memdefont->height; + window = insetrect(gscreen->r, 4); + window.max.y = window.min.y+(Dy(window)/h)*h; + curpos = window.min; +// screenclear(); + + graphicscmap(0); + +// swc = swcurs_create(gscreendata.data, gscreen.width, gscreen.ldepth, gscreen.r, 1); + + drawcursor(nil); +} + +enum { + ScreenCached = 1 /* non-zero if screen region not write-through */ +}; + +void +flushmemscreen(Rectangle r) +{ + if(rectclip(&r, gscreen->r) == 0) + return; + if(r.min.x >= r.max.x || r.min.y >= r.max.y) + return; + if(ScreenCached) + dcflush((ulong*)gscreen->data->bdata + gscreen->width*r.min.y, gscreen->width*Dy(r)); +} + +/* + * export screen to interpreter + */ +uchar* +attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen) +{ + *r = gscreen->r; + *d = gscreen->depth; + *chan = gscreen->chan; + *width = gscreen->width; + *softscreen = ScreenCached; + + return (uchar*)gscreen->data->bdata; +} + +void +detachscreen(void) +{ +} + +/* + * write a string to the screen + */ +void +screenputs(char *s, int n) +{ + int i; + Rune r; + char buf[4]; + + if(novgascreen || xgdata.bdata == nil || memdefont == nil) + return; + if(islo() == 0) { + /* don't deadlock trying to print in interrupt */ + if(!canlock(&screenlock)) + return; + } else + lock(&screenlock); + + while(n > 0) { + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + screenputc(buf); + } + /* Only OK for now */ + flushmemscreen(gscreen->r); + + unlock(&screenlock); +} + +static void +scroll(void) +{ + int o; + Point p; + Rectangle r; + + o = 4*memdefont->height; + r = Rpt(window.min, Pt(window.max.x, window.max.y-o)); + p = Pt(window.min.x, window.min.y+o); + memimagedraw(gscreen, r, gscreen, p, nil, p, SoverD); + flushmemscreen(r); + r = Rpt(Pt(window.min.x, window.max.y-o), window.max); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + + curpos.y -= o; +} + +static void +clearline(void) +{ + Rectangle r; + int yloc = curpos.y; + + r = Rpt(Pt(window.min.x, window.min.y + yloc), + Pt(window.max.x, window.min.y+yloc+memdefont->height)); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); +} + +static void +screenputc(char *buf) +{ + Point p; + int h, w, pos; + Rectangle r; + static int *xp; + static int xbuf[256]; + + h = memdefont->height; + if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) + xp = xbuf; + + switch(buf[0]) { + case '\n': + if(curpos.y+h >= window.max.y) + scroll(); + curpos.y += h; + /* fall through */ + case '\r': + xp = xbuf; + curpos.x = window.min.x; + break; + case '\t': + if(curpos.x == window.min.x) + clearline(); + p = memsubfontwidth(memdefont, " "); + w = p.x; + *xp++ = curpos.x; + pos = (curpos.x-window.min.x)/w; + pos = 8-(pos%8); + r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + curpos.x += pos*w; + break; + case '\b': + if(xp <= xbuf) + break; + xp--; + r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h)); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + flushmemscreen(r); + curpos.x = *xp; + break; + case '\0': + break; + default: + p = memsubfontwidth(memdefont, buf); + w = p.x; + + if(curpos.x >= window.max.x-w) + screenputc("\n"); + + if(curpos.x == window.min.x) + clearline(); + if(xp < xbuf+nelem(xbuf)) + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h); + memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD); + memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf); + flushmemscreen(r); + curpos.x += w; + } +} + +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + ulong x; + + if(gscreen->depth >= 8) + x = 0xFF; + else + x = 0xF; + p &= x; + p ^= x; + lock(&palettelock); + lcdsetrgb(p, r, g, b); + unlock(&palettelock); + return ~0; +} + +void +getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb) +{ + /* TO DO */ + *pr = *pg = *pb = 0; +} + +/* + * See section 5.2.1 (page 5-6) of the MPC823 manual + */ +static uchar lcdclock[17] = { /* (a<<2)|b => divisor of (1<<a)*((b<<1)+1) */ + 0, 0, (1<<2), 1, + (2<<2), 2, (1<<2)|1, 3, + (3<<2), (1<<2)|2, (1<<2)|2, (2<<2)|1, + (2<<2)|1, (1<<2)|3, (1<<2)|3, (4<<2), + (4<<2) +}; + +enum { + /* lccr */ + Enable = 1<<0, + + /* lchcr */ + BigEndian = 1<<24, + AT7 = 7<<21, /* access type */ + + /* sdcr */ + LAM = 1<<6, /* ``LCD aggressive mode'' */ +}; + +/* + * initialise MPC8xx LCD controller incorporating board or display-specific values in Mode.lcd + */ +static void +lcdinit(Mode *mode) +{ + IMM *io; + int i, d; + long hz; + + io = m->iomem; + mode->aperture = xspanalloc(mode->x*mode->y, 16, 0); + mode->apsize = mode->x*mode->y; + + io->sdcr = 1; /* MPC823 errata: turn off LAM before disabling controller */ + eieio(); + io->lcfaa = PADDR(mode->aperture); + io->lccr = (((mode->x*mode->y*(1<<mode->d)+127)/128) << 17) | (mode->d << 5) | mode->lcd.flags; + switch(mode->d){ + default: + case 0: + /* monochrome/greyscale identity map */ + for(i=0; i<16; i++) + io->lcdmap[i] = i; + break; + case 2: + /* 4-bit grey scale map */ + for(i=0; i<16; i++) + io->lcdmap[0] = (i<<8)|(i<<4)|i; + break; + case 3: + /* 8-bit linear map */ + for(i=0; i<256; i++) + io->lcdmap[i] = (i<<8)|(i<<4)|i; + break; + } + + io->lcvcr = (mode->y << 11) | (mode->lcd.vpw<<28) | (mode->lcd.ac<<21) | mode->lcd.wbf; + io->lchcr = (mode->x<<10) | BigEndian | mode->lcd.wbl; + + hz = m->cpuhz; + d = hz/mode->lcd.freq; + if(hz/d > mode->lcd.freq) + d++; + if(d >= 16) + d = 16; + + /* + * enable LCD outputs + */ + io->pddat = 0; + lcdpdpar = 0x1fff & ~mode->lcd.notpdpar; + io->pdpar = lcdpdpar; + io->pddir = 0x1fff; + io->pbpar |= IBIT(31) | IBIT(19) | IBIT(17); + io->pbdir |= IBIT(31) | IBIT(19) | IBIT(17); + io->pbodr &= ~(IBIT(31) | IBIT(19) | IBIT(17)); + + /* + * with the data cache off, early revisions of the 823 did not require + * the `aggressive' DMA priority to avoid flicker, but flicker is obvious + * on the 823A when the cache is on, so LAM is now set + */ + io->sdcr = (io->sdcr & ~0xF) | LAM; /* LAM=1, LAID=0, RAID=0 */ + +// gscreen.width = gscreen.width; /* access external memory before enabling (mpc823 errata) */ + eieio(); + io->sccrk = KEEP_ALIVE_KEY; + eieio(); + io->sccr = (io->sccr & ~0x1F) | lcdclock[d]; + eieio(); + io->sccrk= ~KEEP_ALIVE_KEY; + io->lcsr = 7; /* clear status */ + eieio(); + io->lccr |= Enable; + archbacklight(1); +} + +static void +lcdsetrgb(int p, ulong r, ulong g, ulong b) +{ + r >>= 28; + g >>= 28; + b >>= 28; + m->iomem->lcdmap[p&0xFF] = (r<<8) | (g<<4) | b; +} + +void +blankscreen(int blank) +{ + USED(blank); /* TO DO */ +} + +/* + * enable/disable LCD panel (eg, when using video subsystem) + */ +void +lcdpanel(int on) +{ + IMM *io; + + if(on){ + archbacklight(1); + io = ioplock(); + io->pddat = 0; + io->pdpar = lcdpdpar; + io->pddir = 0x1fff; + io->lccr |= Enable; + iopunlock(); + }else{ + io = ioplock(); + io->sdcr = 1; /* MPC823 errata: turn off LAM before disabling controller */ + eieio(); + io->pddir = 0; + eieio(); + io->lccr &= ~Enable; + iopunlock(); + archbacklight(0); + } +} + +/* + * Software cursor code. Interim version (for baseline). + * we may want to replace code here by memdraw primitives. + */ + +enum { + CUR_ENA = 0x01, /* cursor is enabled */ + CUR_DRW = 0x02, /* cursor is currently drawn */ + CUR_SWP = 0x10, /* bit swap */ + CURSWID = 16, + CURSHGT = 16, +}; + +typedef struct SWcursor { + ulong *fb; /* screen frame buffer */ + Rectangle r; + int d; /* ldepth of screen */ + int width; /* width of screen in ulongs */ + int x; + int y; + int hotx; + int hoty; + uchar cbwid; /* cursor byte width */ + uchar f; /* flags */ + uchar cwid; + uchar chgt; + int hidecount; + uchar data[CURSWID*CURSHGT]; + uchar mask[CURSWID*CURSHGT]; + uchar save[CURSWID*CURSHGT]; +} SWcursor; + +static Rectangle cursoroffrect; +static int cursorisoff; + +static void swcursorflush(int, int); +static void swcurs_draw_or_undraw(SWcursor *); + +static void +cursorupdate0(void) +{ + int inrect, x, y; + Point m; + + m = mousexy(); + x = m.x - swc->hotx; + y = m.y - swc->hoty; + inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x + && y >= cursoroffrect.min.y && y < cursoroffrect.max.y); + if (cursorisoff == inrect) + return; + cursorisoff = inrect; + if (inrect) + swcurs_hide(swc); + else { + swc->hidecount = 0; + swcurs_draw_or_undraw(swc); + } + swcursorflush(m.x, m.y); +} + +void +cursorupdate(Rectangle r) +{ + lock(&screenlock); + r.min.x -= 16; + r.min.y -= 16; + cursoroffrect = r; + if (swc) + cursorupdate0(); + unlock(&screenlock); +} + +void +cursorenable(void) +{ + Point m; + + lock(&screenlock); + if(swc) { + swcurs_enable(swc); + m = mousexy(); + swcursorflush(m.x, m.y); + } + unlock(&screenlock); +} + +void +cursordisable(void) +{ + Point m; + + lock(&screenlock); + if(swc) { + swcurs_disable(swc); + m = mousexy(); + swcursorflush(m.x, m.y); + } + unlock(&screenlock); +} + +void +drawcursor(Drawcursor* c) +{ + Point p; + Cursor curs, *cp; + int j, i, h, bpl; + uchar *bc, *bs, *cclr, *cset; + + if(!swc) + return; + + /* Set the default system cursor */ + if(!c || c->data == nil) + cp = &arrow /*&crosshair_black*/; + else { + cp = &curs; + p.x = c->hotx; + p.y = c->hoty; + cp->offset = p; + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); + + h = (c->maxy-c->miny)/2; + if(h > 16) + h = 16; + + bc = c->data; + bs = c->data + h*bpl; + + cclr = cp->clr; + cset = cp->set; + for(i = 0; i < h; i++) { + for(j = 0; j < 2; j++) { + cclr[j] = bc[j]; + cset[j] = bs[j]; + } + bc += bpl; + bs += bpl; + cclr += 2; + cset += 2; + } + } + + if(swc) { + swcurs_load(swc, cp); + p = mousexy(); + swcursorflush(p.x, p.y); + } +} + +SWcursor* +swcurs_create(ulong *fb, int width, int ldepth, Rectangle r, int bitswap) +{ + SWcursor *swc = (SWcursor*)malloc(sizeof(SWcursor)); + swc->fb = fb; + swc->r = r; + swc->d = ldepth; + swc->width = width; + swc->f = bitswap ? CUR_SWP : 0; + swc->x = swc->y = 0; + swc->hotx = swc->hoty = 0; + swc->hidecount = 0; + return swc; +} + +void +swcurs_destroy(SWcursor *swc) +{ + swcurs_disable(swc); + free(swc); +} + +static void +swcursorflush(int x, int y) +{ + Rectangle r; + + /* XXX a little too paranoid here */ + r.min.x = x-16; + r.min.y = y-16; + r.max.x = x+17; + r.max.y = y+17; + flushmemscreen(r); +} + +static void +swcurs_draw_or_undraw(SWcursor *swc) +{ + uchar *p; + uchar *cs; + int w, vw; + int x1 = swc->r.min.x; + int y1 = swc->r.min.y; + int x2 = swc->r.max.x; + int y2 = swc->r.max.y; + int xp = swc->x - swc->hotx; + int yp = swc->y - swc->hoty; + int ofs; + + if(((swc->f & CUR_ENA) && (swc->hidecount <= 0)) + == ((swc->f & CUR_DRW) != 0)) + return; + w = swc->cbwid*BI2BY/(1 << swc->d); + x1 = xp < x1 ? x1 : xp; + y1 = yp < y1 ? y1 : yp; + x2 = xp+w >= x2 ? x2 : xp+w; + y2 = yp+swc->chgt >= y2 ? y2 : yp+swc->chgt; + if(x2 <= x1 || y2 <= y1) + return; + p = (uchar*)(swc->fb + swc->width*y1) + + x1*(1 << swc->d)/BI2BY; + y2 -= y1; + x2 = (x2-x1)*(1 << swc->d)/BI2BY; + vw = swc->width*BY2WD - x2; + w = swc->cbwid - x2; + ofs = swc->cbwid*(y1-yp)+(x1-xp); + cs = swc->save + ofs; + if((swc->f ^= CUR_DRW) & CUR_DRW) { + uchar *cm = swc->mask + ofs; + uchar *cd = swc->data + ofs; + while(y2--) { + x1 = x2; + while(x1--) { + *p = ((*cs++ = *p) & *cm++) ^ *cd++; + p++; + } + cs += w; + cm += w; + cd += w; + p += vw; + } + } else { + while(y2--) { + x1 = x2; + while(x1--) + *p++ = *cs++; + cs += w; + p += vw; + } + } +} + +void +swcurs_hide(SWcursor *swc) +{ + ++swc->hidecount; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_unhide(SWcursor *swc) +{ + if (--swc->hidecount < 0) + swc->hidecount = 0; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_enable(SWcursor *swc) +{ + swc->f |= CUR_ENA; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_disable(SWcursor *swc) +{ + swc->f &= ~CUR_ENA; + swcurs_draw_or_undraw(swc); +} + +void +swcurs_load(SWcursor *swc, Cursor *c) +{ + int i, k; + uchar *bc, *bs, *cd, *cm; + static uchar bdv[4] = {0,Backgnd,Foregnd,0xff}; + static uchar bmv[4] = {0xff,0,0,0xff}; + int bits = 1<<swc->d; + uchar mask = (1<<bits)-1; + int bswp = (swc->f&CUR_SWP) ? 8-bits : 0; + + bc = c->clr; + bs = c->set; + + swcurs_hide(swc); + cd = swc->data; + cm = swc->mask; + swc->hotx = c->offset.x; + swc->hoty = c->offset.y; + swc->chgt = CURSHGT; + swc->cwid = CURSWID; + swc->cbwid = CURSWID*(1<<swc->d)/BI2BY; + for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) { + uchar bcb = *bc++; + uchar bsb = *bs++; + for(k=0; k<BI2BY;) { + uchar cdv = 0; + uchar cmv = 0; + int z; + for(z=0; z<BI2BY; z += bits) { + int n = ((bsb&(0x80))|((bcb&(0x80))<<1))>>7; + int s = z^bswp; + cdv |= (bdv[n]&mask) << s; + cmv |= (bmv[n]&mask) << s; + bcb <<= 1; + bsb <<= 1; + k++; + } + *cd++ = cdv; + *cm++ = cmv; + } + } + swcurs_unhide(swc); +} + diff --git a/os/mpc/screen.h b/os/mpc/screen.h new file mode 100644 index 00000000..8f8288a8 --- /dev/null +++ b/os/mpc/screen.h @@ -0,0 +1,60 @@ +enum { + Pcolours = 256, /* Palette */ + Pred = 0, + Pgreen = 1, + Pblue = 2, + + Pblack = 0x00, + Pwhite = 0xFF, +}; + +typedef struct Cursor Cursor; +struct Cursor +{ + Point offset; + uchar clr[2*16]; + uchar set[2*16]; +}; + +/* + * MPC8xx LCD controller + */ +typedef struct LCDconfig { + long freq; /* ideal panel frequency in Hz */ + int wbl; /* wait between lines (shift/clk cycles) */ + int vpw; /* vertical sync pulse width (lines) */ + int wbf; /* wait between frames (lines) */ + int ac; /* AC timing (frames) */ + ulong flags; + ulong notpdpar; /* reset mask for pdpar */ +} LCDconfig; + +enum { + /* lccr flags stored in LCDconfig.flags */ + ClockLow = 1<<11, + OELow = 1<<10, + HsyncLow = 1<<9, + VsyncLow = 1<<8, + DataLow = 1<<7, + Passive8 = 1<<4, + DualScan = 1<<3, + IsColour = 1<<2, + IsTFT = 1<<1, +}; + +/* + * physical graphics device properties set by archlcdmode + */ +typedef struct Mode { + int x; + int y; + int d; + + uchar* aperture; + int apsize; + LCDconfig lcd; +} Mode; + +int archlcdmode(Mode*); +extern Point mousexy(void); +extern void blankscreen(int);
\ No newline at end of file diff --git a/os/mpc/spi.c b/os/mpc/spi.c new file mode 100644 index 00000000..ce662533 --- /dev/null +++ b/os/mpc/spi.c @@ -0,0 +1,243 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * basic read/write interface to mpc8xx Serial Peripheral Interface; + * used by devtouch.c and devspi.c + */ + +typedef struct Ctlr Ctlr; + +enum { + /* spi-specific BD flags */ + BDContin= 1<<9, /* continuous mode */ + RxeOV= 1<<1, /* overrun */ + TxeUN= 1<<1, /* underflow */ + BDme= 1<<0, /* multimaster error */ + BDrxerr= RxeOV|BDme, + BDtxerr= TxeUN|BDme, + + /* spmod */ + MLoop= 1<<14, /* loopback mode */ + MClockInv= 1<<13, /* inactive state of SPICLK is high */ + MClockPhs= 1<<12, /* SPCLK starts toggling at beginning of transfer */ + MDiv16= 1<<11, /* use BRGCLK/16 as input to SPI baud rate */ + MRev= 1<<10, /* normal operation */ + MMaster= 1<<9, + MSlave= 0<<9, + MEnable= 1<<8, + /* LEN, PS fields */ + + /* spcom */ + STR= 1<<7, /* start transmit */ + + /* spie */ + MME = 1<<5, + TXE = 1<<4, + BSY = 1<<2, + TXB = 1<<1, + RXB = 1<<0, + + /* port B bits */ + SPIMISO = IBIT(28), /* master mode input */ + SPIMOSI = IBIT(29), /* master mode output */ + SPICLK = IBIT(30), + + /* maximum SPI I/O (can change) */ + Bufsize = 64, +}; + +/* + * SPI software structures + */ + +struct Ctlr { + Lock; + QLock io; + int init; + SPI* spi; + IOCparam* sp; + + BD* rd; + BD* td; + int phase; + Rendez r; + char* txbuf; + char* rxbuf; +}; + +static Ctlr spictlr[1]; + +/* dcflush isn't needed if rxbuf and txbuf allocated in uncached IMMR memory */ +#define DCFLUSH(a,n) + +static void interrupt(Ureg*, void*); + +/* + * called by the reset routine of any driver using the SPI + */ +void +spireset(void) +{ + IMM *io; + SPI *spi; + IOCparam *sp; + CPMdev *cpm; + Ctlr *ctlr; + + ctlr = spictlr; + if(ctlr->init) + return; + ctlr->init = 1; + cpm = cpmdev(CPspi); + spi = cpm->regs; + ctlr->spi = spi; + sp = cpm->param; + if(sp == nil){ + print("SPI: can't allocate new parameter memory\n"); + return; + } + ctlr->sp = sp; + + if(ctlr->rxbuf == nil) + ctlr->rxbuf = cpmalloc(Bufsize, 2); + if(ctlr->txbuf == nil) + ctlr->txbuf = cpmalloc(Bufsize, 2); + + if(ctlr->rd == nil){ + ctlr->rd = bdalloc(1); + ctlr->rd->addr = PADDR(ctlr->rxbuf); + ctlr->rd->length = 0; + ctlr->rd->status = BDWrap; + } + if(ctlr->td == nil){ + ctlr->td = bdalloc(1); + ctlr->td->addr = PADDR(ctlr->txbuf); + ctlr->td->length = 0; + ctlr->td->status = BDWrap|BDLast; + } + + /* select port pins */ + io = ioplock(); + io->pbdir |= SPICLK | SPIMOSI | SPIMISO; + io->pbpar |= SPICLK | SPIMOSI | SPIMISO; + iopunlock(); + + /* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */ + sp = ctlr->sp; + sp->rbase = PADDR(ctlr->rd); + sp->tbase = PADDR(ctlr->td); + sp->rfcr = 0x18; + sp->tfcr = 0x18; + sp->mrblr = Bufsize; + sp->rstate = 0; + sp->rptr = 0; + sp->rbptr = sp->rbase; + sp->rcnt = 0; + sp->tstate = 0; + sp->tbptr = sp->tbase; + sp->tptr = 0; + sp->tcnt = 0; + eieio(); + + spi->spmode = MDiv16 | MRev | MMaster | ((8-1)<<4) | 1; /* 8 bit characters */ + if(0) + spi->spmode |= MLoop; /* internal loop back mode for testing */ + + spi->spie = ~0; /* clear events */ + eieio(); + spi->spim = MME|TXE|BSY|TXB|RXB; + eieio(); + spi->spmode |= MEnable; + + intrenable(VectorCPIC+cpm->irq, interrupt, spictlr, BUSUNKNOWN, "spi"); + +} + +enum { + Idling, + Waitval, + Readyval +}; + +static void +interrupt(Ureg*, void *arg) +{ + int events; + Ctlr *ctlr; + SPI *spi; + + ctlr = arg; + spi = ctlr->spi; + events = spi->spie; + eieio(); + spi->spie = events; + if(events & (MME|BSY|TXE)){ + print("SPI#%x\n", events); + if(ctlr->phase != Idling){ + ctlr->phase = Idling; + wakeup(&ctlr->r); + } + }else if(events & RXB){ + if(ctlr->phase == Waitval){ + ctlr->phase = Readyval; + wakeup(&ctlr->r); + } + } +} + +static int +done(void *a) +{ + return ((Ctlr*)a)->phase != Waitval; +} + +/* + * send `nout' bytes on SPI from `out' and read as many bytes of reply into buffer `in'; + * return the number of bytes received, or -1 on error. + */ +long +spioutin(void *out, long nout, void *in) +{ + Ctlr *ctlr; + int nb, p; + + ctlr = spictlr; + if(nout > Bufsize) + return -1; + qlock(&ctlr->io); + if(waserror()){ + qunlock(&ctlr->io); + return -1; + } + if(ctlr->phase != Idling) + sleep(&ctlr->r, done, ctlr); + memmove(ctlr->txbuf, out, nout); + DCFLUSH(ctlr->txbuf, Bufsize); + DCFLUSH(ctlr->rxbuf, Bufsize); + ilock(ctlr); + ctlr->phase = Waitval; + ctlr->td->length = nout; + ctlr->rd->status = BDEmpty|BDWrap|BDInt; + ctlr->td->status = BDReady|BDWrap|BDLast; + eieio(); + ctlr->spi->spcom = STR; + eieio(); + iunlock(ctlr); + sleep(&ctlr->r, done, ctlr); + nb = ctlr->rd->length; + if(nb > nout) + nb = nout; /* shouldn't happen */ + p = ctlr->phase; + poperror(); + qunlock(&ctlr->io); + if(p != Readyval) + return -1; + memmove(in, ctlr->rxbuf, nb); + return nb; +} diff --git a/os/mpc/trap.c b/os/mpc/trap.c new file mode 100644 index 00000000..843d7ad4 --- /dev/null +++ b/os/mpc/trap.c @@ -0,0 +1,554 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" +#include "../port/error.h" + + +enum +{ + Maxhandler= MaxVector /* max number of interrupt handlers */ +}; + +typedef struct Handler Handler; +struct Handler +{ + void (*r)(Ureg*, void*); + void *arg; + char name[KNAMELEN]; + Handler *next; + ulong nintr; + ulong ticks; + int maxtick; +}; + +struct +{ + Handler *ivec[MaxVector]; + Handler h[Maxhandler]; + int free; + Handler* freelist; +} halloc; + +Instr BREAK = 0x7fe00008; +int (*breakhandler)(Ureg*, Proc*); + +void kernfault(Ureg*, int); + +char *excname[] = +{ + "reserved 0", + "system reset", + "machine check", + "data access", + "instruction access", + "external interrupt", + "alignment", + "program exception", + "floating-point unavailable", + "decrementer", + "i/o controller interface error", + "reserved B", + "system call", + "trace trap", + "floating point assist", + "reserved F", + "software emulation", + "ITLB miss", + "DTLB miss", + "ITLB error", + "DTLB error", + "reserved 15", + "reserved 16", + "reserved 17", + "reserved 18", + "reserved 19", + "reserved 1A", + "reserved 1B", + "data breakpoint", + "instruction breakpoint", + "peripheral breakpoint", + "development port", + /* the following are made up on a program exception */ + "floating point exception", /* 20: FPEXC */ + "illegal instruction", /* 21 */ + "privileged instruction", /* 22 */ + "trap", /* 23 */ + "illegal operation", /* 24 */ + "breakpoint", /* 25 */ +}; + +char *fpcause[] = +{ + "inexact operation", + "division by zero", + "underflow", + "overflow", + "invalid operation", +}; +char *fpexcname(Ureg*, ulong, char*); +#define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */ + + +char *regname[]={ + "CAUSE", "SRR1", + "PC", "GOK", + "LR", "CR", + "XER", "CTR", + "R0", "R1", + "R2", "R3", + "R4", "R5", + "R6", "R7", + "R8", "R9", + "R10", "R11", + "R12", "R13", + "R14", "R15", + "R16", "R17", + "R18", "R19", + "R20", "R21", + "R22", "R23", + "R24", "R25", + "R26", "R27", + "R28", "R29", + "R30", "R31", +}; + +void +sethvec(int v, void (*r)(void)) +{ + ulong *vp, pa, o; + + vp = (ulong*)KADDR(v); + vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */ + vp[1] = 0x7c0802a6; /* MOVW LR, R0 */ + vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */ + pa = PADDR(r); + o = pa >> 25; + if(o != 0 && o != 0x7F){ + /* a branch too far: running from ROM */ + vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ + vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ + vp[5] = 0x7c0803a6; /* MOVW R0, LR */ + vp[6] = 0x4e800021; /* BL (LR) */ + }else + vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ + dcflush(vp, 8*sizeof(ulong)); +} + +void +sethvec2(int v, void (*r)(void)) +{ + ulong *vp; + + vp = (ulong*)KADDR(v); + vp[0] = (18<<26)|((ulong)r&~KSEGM)|2; /* ba */ + dcflush(vp, sizeof(*vp)); +} + +void +trap(Ureg *ur) +{ + int ecode, s; + ulong w; + char buf[ERRMAX]; + + ecode = ur->cause >> 8; + if(ecode < 0 || ecode >= 0x1F) + ecode = 0x1F; + switch(ecode){ + case CDEC: + clockintr(ur); + preemption(1); + break; + + case CMCHECK: + case CDSI: + case CISI: + case CIMISS: + case CDMISS: + case CITLBE: + case CDTLBE: + faultpower(ur); + break; + + case CEMU: + if(up == nil) + goto Default; + if((ulong)(ur+1) != ur->r1) + panic("fp emu stack"); + spllo(); + if(waserror()){ + if(up->type == Interp) + disfault(ur, up->env->errstr); + panic("%s", up->env->errstr); + } + if(fpipower(ur) == 0){ + splhi(); + poperror(); + print("pc=#%lux op=#%8.8lux\n", ur->pc, *(ulong*)ur->pc); + goto Default; + } + poperror(); + break; + + case CPROG: + if(ur->status & (1<<19)) { + ecode = 0x20; + w = ur->pc; + if(ur->status & (1<<16)) + w += 4; + if(*(ulong*)w == 0x7fe00008){ /* tw 31,0,0 */ + if(breakhandler){ + s = (*breakhandler)(ur, up); + if(s == BrkSched){ + if(up){ + up->preempted = 0; + sched(); + splhi(); + } + }else if(s == BrkNoSched){ + if(up){ + up->preempted = 1; /* stop it being preempted until next instruction */ + up->dbgreg = 0; + } + } + break; + } + ecode = 0x1D; /* breakpoint */ + } + } + if(ur->status & (1<<18)) + ecode = 0x21; + if(ur->status & (1<<17)) + ecode = 0x22; + /* FALL THROUGH */ + + Default: + default: + if(up && up->type == Interp) { + spllo(); + snprint(buf, sizeof buf, "sys: trap: %s pc=0x%lux", excname[ecode], ur->pc); + error(buf); + break; + } + print("kernel %s pc=0x%lux\n", excname[ecode], ur->pc); + dumpregs(ur); + dumpstack(); + if(m->machno == 0) + spllo(); + exit(1); + } + + splhi(); +} + +void +spurious(Ureg *ur, void *a) +{ + USED(a); + print("SPURIOUS interrupt pc=0x%lux cause=0x%lux\n", + ur->pc, ur->cause); + panic("bad interrupt"); +} + +#define LEV(n) (((n)<<1)|1) +#define IRQ(n) (((n)<<1)|0) + +Lock veclock; + +void +trapinit(void) +{ + int i; + IMM *io; + + + io = m->iomem; + io->simask = 0; /* mask all */ + io->siel = ~0; /* edge sensitive, wake on all */ + io->cicr = 0; /* disable CPM interrupts */ + io->cipr = ~0; /* clear all interrupts */ + io->cimr = 0; /* mask all events */ + io->cicr = (0xE1<<16)|(CPIClevel<<13)|(0x1F<<8); + io->cicr |= 1 << 7; /* enable */ + io->tbscrk = KEEP_ALIVE_KEY; + io->tbscr = 1; /* TBE */ + io->simask |= 1<<(31-LEV(CPIClevel)); /* CPM's level */ + io->tbk = KEEP_ALIVE_KEY; + eieio(); + putdec(~0); + + /* + * set all exceptions to trap + */ + for(i = 0x0; i < 0x3000; i += 0x100) + sethvec(i, trapvec); + + sethvec(CEI<<8, intrvec); + //sethvec2(CIMISS<<8, itlbmiss); + //sethvec2(CDMISS<<8, dtlbmiss); +} + +void +intrenable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name) +{ + Handler *h; + IMM *io; + + v -= VectorPIC; + if(v < 0 || v >= nelem(halloc.ivec)) + panic("intrenable(%d)", v+VectorPIC); + ilock(&veclock); + if((h = halloc.freelist) == nil){ + if(halloc.free >= Maxhandler){ + iunlock(&veclock); + panic("out of interrupt handlers"); + } + h = &halloc.h[halloc.free++]; + }else + halloc.freelist = h->next; + h->r = r; + h->arg = arg; + strncpy(h->name, name, KNAMELEN-1); + h->name[KNAMELEN-1] = 0; + h->next = halloc.ivec[v]; + halloc.ivec[v] = h; + + /* + * enable corresponding interrupt in SIU/CPM + */ + + eieio(); + io = m->iomem; + if(v >= VectorCPIC){ + v -= VectorCPIC; + io->cimr |= 1<<(v&0x1F); + } + else if(v >= VectorIRQ) + io->simask |= 1<<(31-IRQ(v&7)); + else + io->simask |= 1<<(31-LEV(v)); + eieio(); + iunlock(&veclock); +} + +static void +irqdisable(int v) +{ + IMM *io; + + io = m->iomem; + if(v >= VectorCPIC){ + v -= VectorCPIC; + io->cimr &= ~(1<<(v&0x1F)); + } + else if(v >= VectorIRQ) + io->simask &= ~(1<<(31-IRQ(v&7))); + else + io->simask &= ~(1<<(31-LEV(v))); +} + +void +intrdisable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name) +{ + Handler *h, **hp; + + v -= VectorPIC; + if(v < 0 || v >= nelem(halloc.ivec)) + panic("intrenable(%d)", v+VectorPIC); + ilock(&veclock); + for(hp = &halloc.ivec[v]; (h = *hp) != nil; hp = &h->next) + if(h->r == r && h->arg == arg && strcmp(h->name, name) == 0){ + *hp = h->next; + h->next = halloc.freelist; + halloc.freelist = h; + break; + } + if(halloc.ivec[v] == nil) + irqdisable(v); + iunlock(&veclock); +} + +/* + * called directly by l.s:/intrvec. on a multiprocessor we'd need to lock veclock. + */ +void +intr(Ureg *ur) +{ + int b, v; + IMM *io; + Handler *h; + long t0; + Proc *oup; + + ur->cause &= ~0xff; + io = m->iomem; + b = io->sivec>>2; + v = b>>1; + if(b & 1) { + if(v == CPIClevel){ + io->civr = 1; + eieio(); + v = VectorCPIC+(io->civr>>11); + } + }else{ + v += VectorIRQ; + if(io->siel & (1<<(31-b))){ + io->sipend |= 1<<(31-b); + eieio(); + } + } + + ur->cause |= v; + h = halloc.ivec[v]; + if(h == nil){ + iprint("unknown interrupt %d pc=0x%lux\n", v, ur->pc); + irqdisable(v); + return; + } + + /* + * call the interrupt handlers + */ + oup = up; + up = nil; /* no process at interrupt level */ + do { + h->nintr++; + t0 = getdec(); + (*h->r)(ur, h->arg); + t0 -= getdec(); + h->ticks += t0; + if(h->maxtick < t0) + h->maxtick = t0; + h = h->next; + } while(h != nil); + if(v >= VectorCPIC) + io->cisr |= 1<<(v-VectorCPIC); + eieio(); + up = oup; + preemption(0); +} + +int +intrstats(char *buf, int bsize) +{ + Handler *h; + int i, n; + + n = 0; + for(i=0; i<nelem(halloc.ivec) && n < bsize; i++) + if((h = halloc.ivec[i]) != nil && h->nintr) + n += snprint(buf+n, bsize-n, "%3d %lud %lud %ud\n", i, h->nintr, h->ticks, h->maxtick); + return n; +} + +char* +fpexcname(Ureg *ur, ulong fpscr, char *buf) +{ + int i; + char *s; + ulong fppc; + + fppc = ur->pc; + s = 0; + fpscr >>= 3; /* trap enable bits */ + fpscr &= (fpscr>>22); /* anded with exceptions */ + for(i=0; i<5; i++) + if(fpscr & (1<<i)) + s = fpcause[i]; + if(s == 0) + return "no floating point exception"; + sprint(buf, "%s fppc=0x%lux", s, fppc); + return buf; +} + +#define KERNPC(x) (KTZERO<(ulong)(x)&&(ulong)(x)<(ulong)etext) + +void +kernfault(Ureg *ur, int code) +{ + Label l; + + print("panic: kfault %s dar=0x%lux\n", excname[code], getdar()); + print("u=0x%lux status=0x%lux pc=0x%lux sp=0x%lux\n", + up, ur->status, ur->pc, ur->sp); + dumpregs(ur); + l.sp = ur->sp; + l.pc = ur->pc; + dumpstack(); + setpri(PriBackground); /* Let the debugger in */ + for(;;) + sched(); +} + +void +dumpstack(void) +{ + ulong l, v; + int i; + + if(up == 0) + return; + i = 0; + for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){ + v = *(ulong*)l; + if(KTZERO < v && v < (ulong)etext){ + print("%lux=%lux, ", l, v); + if(i++ == 4){ + print("\n"); + i = 0; + } + } + } +} + +void +dumpregs(Ureg *ur) +{ + int i; + ulong *l; + if(up) { + print("registers for %s %ld\n", up->text, up->pid); + if(ur->usp < (ulong)up->kstack || + ur->usp > (ulong)up->kstack+KSTACK) + print("invalid stack ptr\n"); + } + else + print("registers for kernel\n"); + + l = &ur->cause; + for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2) + print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]); +} + +static void +linkproc(void) +{ + spllo(); + (*up->kpfun)(up->arg); + pexit("", 0); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK; + + p->kpfun = func; + p->arg = arg; +} + +void +setpanic(void) +{ + consoleprint = 1; +} + +void +dumplongs(char*, ulong*, int) +{ +} diff --git a/os/mpc/usb.h b/os/mpc/usb.h new file mode 100644 index 00000000..1d750f3f --- /dev/null +++ b/os/mpc/usb.h @@ -0,0 +1,62 @@ +/* + * USB packet definitions + */ + +#define GET2(p) ((((p)[1]&0xFF)<<8)|((p)[0]&0xFF)) +#define PUT2(p,v) {((p)[0] = (v)); ((p)[1] = (v)>>8);} + +enum { + /* request type */ + RH2D = 0<<7, + RD2H = 1<<7, + Rstandard = 0<<5, + Rclass = 1<<5, + Rvendor = 2<<5, + Rdevice = 0, + Rinterface = 1, + Rendpt = 2, + Rother = 3, + + /* standard requests */ + GET_STATUS = 0, + CLEAR_FEATURE = 1, + SET_FEATURE = 3, + SET_ADDRESS = 5, + GET_DESCRIPTOR = 6, + SET_DESCRIPTOR = 7, + GET_CONFIGURATION = 8, + SET_CONFIGURATION = 9, + GET_INTERFACE = 10, + SET_INTERFACE = 11, + SYNCH_FRAME = 12, + + /* hub class feature selectors */ + C_HUB_LOCAL_POWER = 0, + C_HUB_OVER_CURRENT, + PORT_CONNECTION = 0, + PORT_ENABLE = 1, + PORT_SUSPEND = 2, + PORT_OVER_CURRENT = 3, + PORT_RESET = 4, + PORT_POWER = 8, + PORT_LOW_SPEED = 9, + C_PORT_CONNECTION = 16, + C_PORT_ENABLE, + C_PORT_SUSPEND, + C_PORT_OVER_CURRENT, + C_PORT_RESET, + + /* descriptor types */ + DEVICE = 1, + CONFIGURATION = 2, + STRING = 3, + INTERFACE = 4, + ENDPOINT = 5, + HID = 0x21, + REPORT = 0x22, + PHYSICAL = 0x23, + + /* feature selectors */ + DEVICE_REMOTE_WAKEUP = 1, + ENDPOINT_STALL = 0, +}; |
