Charles.Forsyth20060303644188019 years agoHistory
695 lines · 14.2 KB · C
RawDownload
#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.