summaryrefslogtreecommitdiff
path: root/os/mpc
diff options
context:
space:
mode:
Diffstat (limited to 'os/mpc')
-rw-r--r--os/mpc/800io.h666
-rw-r--r--os/mpc/NOTICE3
-rw-r--r--os/mpc/clock.c145
-rw-r--r--os/mpc/cpm.c695
-rw-r--r--os/mpc/cpmtimer.c179
-rw-r--r--os/mpc/devata.c1194
-rw-r--r--os/mpc/devbench.c243
-rw-r--r--os/mpc/devboot.c132
-rw-r--r--os/mpc/devether.c617
-rw-r--r--os/mpc/devpcmcia.c1076
-rw-r--r--os/mpc/devrtc.c258
-rw-r--r--os/mpc/devtouch.c497
-rw-r--r--os/mpc/devuart.c1450
-rw-r--r--os/mpc/dsp.c289
-rw-r--r--os/mpc/dsp.h62
-rw-r--r--os/mpc/etherif.h37
-rw-r--r--os/mpc/etherscc.c528
-rw-r--r--os/mpc/faultpower.c49
-rw-r--r--os/mpc/fp.s205
-rw-r--r--os/mpc/fpi.h61
-rw-r--r--os/mpc/fpipower.c970
-rw-r--r--os/mpc/i2c.c439
-rw-r--r--os/mpc/i2c_spi.srx149
-rw-r--r--os/mpc/inb.s120
-rw-r--r--os/mpc/kbd.c20
-rw-r--r--os/mpc/l.s685
-rw-r--r--os/mpc/nofp.s31
-rw-r--r--os/mpc/pcmcia.h19
-rw-r--r--os/mpc/pit.c68
-rw-r--r--os/mpc/powerbreak.c123
-rw-r--r--os/mpc/rmap.c106
-rw-r--r--os/mpc/screen.c832
-rw-r--r--os/mpc/screen.h60
-rw-r--r--os/mpc/spi.c243
-rw-r--r--os/mpc/trap.c554
-rw-r--r--os/mpc/usb.h62
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, &timesync[i], 0);
+ for(i=0; i<np; i++){
+ timesync[i].flag = 1;
+ wakeup(&timesync[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 = &ether->f[Ntypes];
+
+ multi = pkt->d[0] & 1;
+ /* check for valid multcast addresses */
+ if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
+ if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+ if(fromwire){
+ freeb(bp);
+ bp = 0;
+ }
+ return bp;
+ }
+ }
+
+ /* is it for me? */
+ tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+ fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+ /*
+ * Multiplex the packet to all the connections which want it.
+ * If the packet is not to be used subsequently (fromwire != 0),
+ * attempt to simply pass it into one of the connections, thereby
+ * saving a copy of the data (usual case hopefully).
+ */
+ for(fp = ether->f; fp < ep; fp++){
+ if((f = *fp) && (f->type == type || f->type < 0))
+ if(tome || multi || f->prom){
+ /* Don't want to hear bridged packets */
+ if(f->bridge && !fromwire && !fromme)
+ continue;
+ if(!f->headersonly){
+ if(fromwire && fx == 0)
+ fx = f;
+ else if(xbp = iallocb(len)){
+ memmove(xbp->wp, pkt, len);
+ xbp->wp += len;
+ if(qpass(f->in, xbp) < 0)
+ ether->soverflows++;
+ }
+ else
+ ether->soverflows++;
+ }
+ else
+ etherrtrace(f, pkt, len);
+ }
+ }
+
+ if(fx){
+ if(qpass(fx->in, bp) < 0)
+ ether->soverflows++;
+ return 0;
+ }
+ if(fromwire){
+ freeb(bp);
+ return 0;
+ }
+
+ return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+ int len, loopback, s;
+ Etherpkt *pkt;
+
+ ether->outpackets++;
+
+ /*
+ * Check if the packet has to be placed back onto the input queue,
+ * i.e. if it's a loopback or broadcast packet or the interface is
+ * in promiscuous mode.
+ * If it's a loopback packet indicate to etheriq that the data isn't
+ * needed and return, etheriq will pass-on or free the block.
+ * To enable bridging to work, only packets that were originated
+ * by this interface are fed back.
+ */
+ pkt = (Etherpkt*)bp->rp;
+ len = BLEN(bp);
+ loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+ if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+ s = splhi();
+ etheriq(ether, bp, 0);
+ splx(s);
+ }
+
+ if(!loopback){
+ qbwrite(ether->oq, bp);
+ if(ether->transmit != nil)
+ ether->transmit(ether);
+ }else
+ freeb(bp);
+
+ return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+ Ether *ether;
+ Block *bp;
+ int onoff;
+ Cmdbuf *cb;
+ long l;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ if(NETTYPE(chan->qid.path) != Ndataqid) {
+ l = netifwrite(ether, chan, buf, n);
+ if(l >= 0)
+ goto out;
+ cb = parsecmd(buf, n);
+ if(strcmp(cb->f[0], "nonblocking") == 0){
+ if(cb->nf <= 1)
+ onoff = 1;
+ else
+ onoff = atoi(cb->f[1]);
+ qnoblock(ether->oq, onoff);
+ free(cb);
+ goto out;
+ }
+ free(cb);
+ if(ether->ctl!=nil){
+ l = ether->ctl(ether,buf,n);
+ goto out;
+ }
+ error(Ebadctl);
+ }
+
+ if(n > ether->maxmtu)
+ error(Etoobig);
+ if(n < ether->minmtu)
+ error(Etoosmall);
+ bp = allocb(n);
+ if(waserror()){
+ freeb(bp);
+ nexterror();
+ }
+ memmove(bp->rp, buf, n);
+ memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+ bp->wp += n;
+ poperror();
+
+ l = etheroq(ether, bp);
+out:
+ poperror();
+ runlock(ether);
+ return l;
+}
+
+static long
+etherbwrite(Chan* chan, Block* bp, ulong)
+{
+ Ether *ether;
+ long n;
+
+ n = BLEN(bp);
+ if(NETTYPE(chan->qid.path) != Ndataqid){
+ if(waserror()) {
+ freeb(bp);
+ nexterror();
+ }
+ n = etherwrite(chan, bp->rp, n, 0);
+ poperror();
+ freeb(bp);
+ return n;
+ }
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ if(n > ether->maxmtu){
+ freeb(bp);
+ error(Etoobig);
+ }
+ if(n < ether->minmtu){
+ freeb(bp);
+ error(Etoosmall);
+ }
+ n = etheroq(ether, bp);
+ poperror();
+ runlock(ether);
+ return n;
+}
+
+static struct {
+ char* type;
+ int (*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+ static int ncard;
+
+ if(ncard == MaxEther)
+ panic("too many ether cards");
+ cards[ncard].type = t;
+ cards[ncard].reset = r;
+ ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+ char nip[4];
+ char *p;
+ int i;
+
+ p = from;
+ for(i = 0; i < Eaddrlen; i++){
+ if(*p == 0)
+ return -1;
+ nip[0] = *p++;
+ if(*p == 0)
+ return -1;
+ nip[1] = *p++;
+ nip[2] = 0;
+ to[i] = strtoul(nip, 0, 16);
+ if(*p == ':')
+ p++;
+ }
+ return 0;
+}
+
+static void
+etherreset(void)
+{
+ Ether *ether;
+ int i, n, ctlrno;
+ char name[KNAMELEN], buf[128];
+
+ for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+ if(ether == 0)
+ ether = malloc(sizeof(Ether));
+ memset(ether, 0, sizeof(Ether));
+ ether->ctlrno = ctlrno;
+ ether->mbps = 10;
+ ether->minmtu = ETHERMINTU;
+ ether->maxmtu = ETHERMAXTU;
+ ether->tbdf = BUSUNKNOWN;
+
+ if(archether(ctlrno, ether) <= 0)
+ continue;
+
+ for(n = 0; cards[n].type; n++){
+ if(cistrcmp(cards[n].type, ether->type))
+ continue;
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
+ if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+ memset(ether->ea, 0, Eaddrlen);
+ }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
+ cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
+ ether->fullduplex = 1;
+ else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+ ether->mbps = 100;
+ }
+ if(cards[n].reset(ether))
+ break;
+ snprint(name, sizeof(name), "ether%d", ctlrno);
+
+ if(ether->interrupt != nil)
+ intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
+
+ i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
+ ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
+ if(ether->mem)
+ i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+ if(ether->size)
+ i += sprint(buf+i, " size 0x%luX", ether->size);
+ i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
+ ether->ea[0], ether->ea[1], ether->ea[2],
+ ether->ea[3], ether->ea[4], ether->ea[5]);
+ sprint(buf+i, "\n");
+ print(buf);
+
+ if(ether->mbps == 100){
+ netifinit(ether, name, Ntypes, 256*1024);
+ if(ether->oq == 0)
+ ether->oq = qopen(256*1024, Qmsg, 0, 0);
+ }
+ else{
+ netifinit(ether, name, Ntypes, 64*1024);
+ if(ether->oq == 0)
+ ether->oq = qopen(64*1024, Qmsg, 0, 0);
+ }
+ if(ether->oq == 0)
+ panic("etherreset %s", name);
+ ether->alen = Eaddrlen;
+ memmove(ether->addr, ether->ea, Eaddrlen);
+ memset(ether->bcast, 0xFF, Eaddrlen);
+
+ etherxx[ctlrno] = ether;
+ ether = 0;
+ break;
+ }
+ }
+ if(ether)
+ free(ether);
+}
+
+static void
+etherpower(int on)
+{
+ int i;
+ Ether *ether;
+
+ for(i = 0; i < MaxEther; i++){
+ if((ether = etherxx[i]) == nil || ether->power == nil)
+ continue;
+ if(on){
+ if(canrlock(ether))
+ continue;
+ if(ether->power != nil)
+ ether->power(ether, on);
+ wunlock(ether);
+ }else{
+ if(!canrlock(ether))
+ continue;
+ wlock(ether);
+ if(ether->power != nil)
+ ether->power(ether, on);
+ /* Keep locked until power goes back on */
+ }
+ }
+}
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+ int i, j;
+ ulong crc, b;
+
+ crc = 0xffffffff;
+ for(i = 0; i < len; i++){
+ b = *p++;
+ for(j = 0; j < 8; j++){
+ crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+ b >>= 1;
+ }
+ }
+ return crc;
+}
+
+Dev etherdevtab = {
+ 'l',
+ "ether",
+
+ etherreset,
+ devinit,
+ ethershutdown,
+ etherattach,
+ etherwalk,
+ etherstat,
+ etheropen,
+ ethercreate,
+ etherclose,
+ etherread,
+ etherbread,
+ etherwrite,
+ etherbwrite,
+ devremove,
+ etherwstat,
+ etherpower,
+};
diff --git a/os/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,
+};