#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 */voidcpminit(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 */voidcpmop(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 */voidiopunlock(void){ eieio(); iunlock(&cpmlock);} /* * connect SCCx clocks in NSMI mode (x=1 for USB) */voidsccnmsi(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 */voidsmcnmsi(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. */intcpmidopen(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 */voidcpmidclose(int id){ Chanuse *use; use = &cpmids[id]; ilock(use); use->owner = nil; iunlock(use);} /* * if SCC d is currently enabled, shut it down */voidsccxstop(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 */voidsmcxstop(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 */voidbdfree(BD *b, int n){ if(b){ eieio(); mapfree(&bdmap, PADDR(b), n*sizeof(BD)); }} /* * print a buffer descriptor and its data (when debugging) */voiddumpbd(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 */voidcpmfree(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) */intbrgalloc(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 */voidbrgfree(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. */ulongbaudgen(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. */intioringinit(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;Lines 1–500 of 695. Load more
#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;