Charles.Forsyth20060303644188019 years agoHistory
243 lines · 4.5 KB · C
RawDownload
#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 */voidspireset(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 voidinterrupt(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 intdone(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. */longspioutin(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;}