diff options
Diffstat (limited to 'os/mpc/cpm.c')
| -rw-r--r-- | os/mpc/cpm.c | 695 |
1 files changed, 695 insertions, 0 deletions
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; +} |
