Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/mpc/spi.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname mpc/spi.c 2000/0516
## diff -e /dev/null /n/emeliedump/2000/0516/sys/src/9/mpc/spi.c
0a
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

typedef struct SPIparam SPIparam;
struct SPIparam {
	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;
};

enum {
	Nrdre		= 2,	/* receive descriptor ring entries */
	Ntdre		= 2,	/* transmit descriptor ring entries */

	Rbsize		= 8,		/* ring buffer size (+3 for PID+CRC16) */
	Bufsize		= (Rbsize+7)&~7,	/* aligned */
};

enum {
	/* spi-specific BD flags */
	BDContin=	1<<9,	/* continuous mode */
	BDrxov=		1<<1,	/* overrun */
	BDtxun=		1<<1,	/* underflow */
	BDme=		1<<0,	/* multimaster error */
	BDrxerr=		BDrxov|BDme,
	BDtxerr=		BDtxun|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 */

	/* spie */
	MME =	1<<5,
	TXE =	1<<4,
	RES =	1<<3,
	BSY =	1<<2,
	TXB =	1<<1,
	RXB =	1<<0,
};

/*
 * software structures
 */
typedef struct Ctlr Ctlr;

struct Ctlr {
	QLock;
	int	init;
	SPI*	spi;
	SPIparam*	sp;

	int	rxintr;
	Rendez	ir;
	Ring;
};

static	Ctlr	spictlr[1];

static	void	interrupt(Ureg*, void*);

/*
 * test driver for SPI, host mode
 */
void
spireset(void)
{
	IMM *io;
	SPI *spi;
	SPIparam *sp;
	Ctlr *ctlr;

	io = m->iomem;
	sp = (SPIparam*)cpmparam(SPIP, sizeof(*sp));
	memset(sp, 0, sizeof(*sp));
	if(sp == nil){
		print("SPI: can't allocate new parameter memory\n");
		return;
	}

	spi = (SPI*)((ulong)m->iomem+0xAA0);
	ctlr = spictlr;

	/* step 1: select port pins */
	io->pbdir |= IBIT(30)|IBIT(29)|IBIT(28);
	io->pbpar |= IBIT(30)|IBIT(29)|IBIT(28);	/* SPICLK, SPIMOSI, SPIMISO */

	/* step 2: set CS pin - also disables SPISEL */
	io->pbpar &= ~(IBIT(31));
	io->pbdir |= IBIT(31);
	io->pbodr &= ~(IBIT(31));
	io->pbdat &= ~(IBIT(31));

	ctlr->spi = spi;
	ctlr->sp = sp;

	if(ioringinit(ctlr, Nrdre, Ntdre, Bufsize)<0)
		panic("spireset: ioringinit");

	/* step 3: configure rbase and tbase */
	sp->rbase = PADDR(ctlr->rdr);
	sp->tbase = PADDR(ctlr->tdr);

	/* step 4: do not issue InitRxTx command - due to microcode bug */

	/* step 5: */
	io->sdcr = 1;

	/* step 6: */
	sp->rfcr = 0x10;
	sp->tfcr = 0x10;

	/* step 7: */
	sp->mrblr = Bufsize;

	/* step 7.5: init other params because of bug in microcode */
	sp->rstate = 0;
	sp->rbptr = sp->rbase;
	sp->rcnt = 0;
	sp->tstate = 0;
	sp->tbptr = sp->tbase;
	sp->tcnt = 0;

	/* step 8-9: done by ioringinit */

	/* step 10: clear events */
	spi->spie = ~0;	

	/* step 11-12: enable interrupts  */
	intrenable(VectorCPIC+0x05, interrupt, spictlr, BUSUNKNOWN);
	spi->spim = ~0;

	/* step 13: enable in master mode, 8 bit chacters, slow clock */
	spi->spmode = MMaster|MRev|MDiv16|MEnable|(0x7<<4)|0xf;
print("spi->spmode = %ux\n", spi->spmode);
}

static void
interrupt(Ureg*, void *arg)
{
	int events;
	Ctlr *ctlr;
	SPI *spi;

	ctlr = arg;
	spi = ctlr->spi;
	events = spi->spie;
	spi->spie = events;
	print("SPI#%x\n", events);
}

void
spdump(SPIparam *sp)
{
	print("rbase = %ux\n", sp->rbase);
	print("tbase = %ux\n", sp->tbase);
	print("rfcr = %ux\n", sp->rfcr);
	print("tfcr = %ux\n", sp->tfcr);
	print("mrblr = %ux\n", sp->mrblr);
	print("rstate = %lux\n", sp->rstate);
	print("rptr = %lux\n", sp->rptr);
	print("rbptr = %ux\n", sp->rbptr);
	print("rcnt = %ux\n", sp->rcnt);
	print("rtmp = %ux\n", sp->rtmp);
	print("tstate = %lux\n", sp->tstate);
	print("tptr = %lux\n", sp->tptr);
	print("tbptr = %ux\n", sp->tbptr);
	print("tcnt = %ux\n", sp->tcnt);
	print("ttmp = %ux\n", sp->ttmp);
}

void
spitest(void)
{
	BD *dre;
	Block *b;
	int len;
	Ctlr *ctlr;
	ulong status;

	ctlr = spictlr;

	dre = &ctlr->tdr[ctlr->tdrh];
	if(dre->status & BDReady)
		panic("spitest: txstart");

print("dre->status %ux %ux\n", dre[0].status, dre[1].status);	
	b = allocb(4);
	b->wp[0] = 0xc0;
	b->wp[1] = 0x00;
	b->wp[2] = 0x00;
	b->wp[3] = 0x00;
	b->wp += 4;
	len = BLEN(b);
print("len = %d\n", len);
	dcflush(b->rp, len);
	if(ctlr->txb[ctlr->tdrh] != nil)
		panic("scc/ether: txstart");
	ctlr->txb[ctlr->tdrh] = b;
	dre->addr = PADDR(b->rp);
print("addr = %lux\n", PADDR(b->rp));
	dre->length = len;
	dre->status = (dre->status & BDWrap) | BDReady|BDInt|BDLast;
	eieio();
spdump(ctlr->sp);
m->iomem->pbdat |= IBIT(31);
microdelay(1);
	ctlr->spi->spcom = 1<<7;	/* transmit now */
print("cpcom = %ux\n", &ctlr->spi->spcom);
spdump(ctlr->sp);
	eieio();
	ctlr->ntq++;
	ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);
print("going into loop %ux %ux\n", dre->status, ctlr->spi->spcom);
	while(dre->status&BDReady) {
print("ctrl->spi->spim = %ux %ux\n", ctlr->spi->spie, dre->status);
spdump(ctlr->sp);
		delay(10000);
	}
delay(100);
	dre = &ctlr->rdr[ctlr->rdrx];
	status = dre->status;
	len = dre->length;
print("%d status = %ux len=%d\n", ctlr->rdrx, status, len);
	b = iallocb(len);
	memmove(b->wp, KADDR(dre->addr), len);
	b->wp += len;
print("%ux %ux %ux %ux\n", b->rp[0], b->rp[1], b->rp[2], b->rp[3]);
	dcflush(KADDR(dre->addr), len);
	dre->status = (status & BDWrap) | BDEmpty | BDInt;
	ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
m->iomem->pbdat &= ~(IBIT(31));
microdelay(1);
}


void
spiinit(void)
{
	spireset();
	spitest();
}

#ifdef XXX

static void
epprod(Ctlr *ctlr, int epn)
{
	ctlr->spi->uscom = FifoFill | (epn&3);
	eieio();
}

static void
txstart(Endpt *r)
{
	int len, flags;
	Block *b;
	BD *dre, *rdy;

	if(r->ctlr->init)
		return;
	rdy = nil;
	while(r->ntq < Ntdre-1){
		b = qget(r->oq);
		if(b == 0)
			break;

		dre = &r->tdr[r->tdrh];
		if(dre->status & BDReady)
			panic("txstart");
dumpspi(b, "TX");
	
		/*
		 * Give ownership of the descriptor to the chip, increment the
		 * software ring descriptor pointer and tell the chip to poll.
		 */
		flags = 0;
		switch(b->rp[0]){
		case TokDATA1:
			flags = BDData1|BDData0;
		case TokDATA0:
			flags |= BDtxcrc|BDcnf|BDReady;
			flags |= BDData0;
			b->rp++;	/* skip DATAn */
		}
		len = BLEN(b);
		dcflush(b->rp, len);
		if(r->txb[r->tdrh] != nil)
			panic("spi: txstart");
		r->txb[r->tdrh] = b;
		dre->addr = PADDR(b->rp);
		dre->length = len;
		eieio();
		dre->status = (dre->status & BDWrap) | BDInt|BDLast | flags;
		if(rdy){
			rdy->status |= BDReady;
			rdy = nil;
		}
		if((flags & BDReady) == 0)
			rdy = dre;
		eieio();
		r->ntq++;
		r->tdrh = NEXT(r->tdrh, Ntdre);
	}
	if(rdy)
		rdy->status |= BDReady;
	eieio();
}

static void
transmit(Endpt *r)
{
	ilock(r->ctlr);
	txstart(r);
	iunlock(r->ctlr);
}

static void
endptintr(Endpt *r, int events)
{
	int len, status;
	BD *dre;
	Block *b;

	if(events & Frxb || 1){
		dre = &r->rdr[r->rdrx];
		while(((status = dre->status) & BDEmpty) == 0){
			if(status & BDrxerr || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){
				print("spi rx: %4.4ux %d ", status, dre->length);
				{uchar *p;int i; p=KADDR(dre->addr); for(i=0;i<14&&i<dre->length; i++)print(" %.2ux", p[i]);print("\n");}
			}
			else{
				/*
				 * We have a packet. Read it into the next
				 * free ring buffer, if any.
				 */
				len = dre->length-2;	/* discard CRC */
				if(len >= 0 && (b = iallocb(len)) != 0){
					memmove(b->wp, KADDR(dre->addr), len);
					dcflush(KADDR(dre->addr), len);
					b->wp += len;
					// spiiq(ctlr, b);
					dumpspi(b, "RX");
					freeb(b);
				}
			}

			/*
			 * Finished with this descriptor, reinitialise it,
			 * give it back to the chip, then on to the next...
			 */
			dre->length = 0;
			dre->status = (dre->status & BDWrap) | BDEmpty | BDInt;
			eieio();

			r->rdrx = NEXT(r->rdrx, Nrdre);
			dre = &r->rdr[r->rdrx];
			r->rxintr = 1;
			wakeup(&r->ir);
		}
	}

	/*
	 * Transmitter interrupt: handle anything queued for a free descriptor.
	 */
	if(events & (Ftxb|Ftxe0|Ftxe1)){
		lock(r->ctlr);
		while(r->ntq){
			dre = &r->tdr[r->tdri];
			if(dre->status & BDReady)
				break;
			print("spitx=#%.4x %8.8lux\n", dre->status, dre->addr);
			/* TO DO: error counting */
			b = r->txb[r->tdri];
			if(b == nil)
				panic("spi/interrupt: bufp");
			r->txb[r->tdri] = nil;
			freeb(b);
			r->ntq--;
			r->tdri = NEXT(r->tdri, Ntdre);
		}
		txstart(r);
		unlock(r->ctlr);
		if(r->ntq)
			epprod(r->ctlr, r->x);
	}
}


static void
dumpspi(Block *b, char *msg)
{
	int i;

	print("%s: %8.8lux [%d]: ", msg, (ulong)b->rp, BLEN(b));
	for(i=0; i<BLEN(b) && i < 16; i++)
		print(" %.2x", b->rp[i]);
	print("\n");
}

static void
setaddr(Endpt *e, int dev, int endpt)
{
	ushort v;

	e->dev = dev;
	e->endpt = endpt;
	v = (e->endpt<<7) | e->dev;
	v |= crc5(v) << 11;
	e->addr[0] = v;
	e->addr[1] = v>>8;
}

static int
spiinput(void *a)
{
	return ((Ctlr*)a)->rxintr;
}

static void
spitest(void)
{
	Ctlr *ctlr;
	Endpt *e, *e1;
	uchar msg[8];
	int epn, testing, i;

	ctlr = spictlr;
	epn = 0;
	testing = ctlr->spi->usmod & TEST;
	if(testing)
		epn = 1;
	e = &ctlr->pts[0];
	e1 = &ctlr->pts[1];
	memset(msg, 0, 8);
	msg[0] = 0;
	msg[1] = 8;	/* set configuration */
	msg[2] = 1;
	setaddr(e, 0, epn);
	sendtoken(e, TokSETUP);
	senddata(e, msg, 8);
	if(!testing){
		for(i=0; i<8; i++)
			msg[i] = 0x40+i;
		senddata(e1, msg, 8);
		e->rxintr = 0;
		sendtoken(e, TokIN);
		transmit(e);
		epprod(ctlr, 0);
		tsleep(&e->ir, spiinput, e, 1000);
		if(!spiinput(e))
			print("RX: none\n");
	}
	sendtoken(e, TokSETUP);
	msg[0] = 0x23;
	msg[1] = 3;	/* set feature */
	msg[2] = 8;	/* port power */
	msg[3] = 0;
	msg[4] = 1;	/* port 1 */
	senddata(e, msg, 8);
	if(!testing){
		for(i=0; i<8; i++)
			msg[i] = 0x60+i;
		senddata(e1, msg, 8);
		e->rxintr = 0;
		sendtoken(e, TokIN);
		transmit(e);
		epprod(ctlr, 0);
		tsleep(&e->ir, spiinput, e, 1000);
		if(!spiinput(e))
			print("RX: none\n");
	}
}

static void
setupep(int n, EPparam *ep)
{
	Endpt *e;

	e = &usbctlr->pts[n];
	e->x = n;
	e->ctlr = usbctlr;
	e->xtog = TokDATA0;
	if(e->oq == nil)
		e->oq = qopen(8*1024, 1, 0, 0);
	if(e->iq == nil)
		e->iq = qopen(8*1024, 1, 0, 0);
	e->ep = ep;
	if(ioringinit(e, Nrdre, Ntdre, Bufsize) < 0)
		panic("usbreset");
	ep->rbase = PADDR(e->rdr);
	ep->rbptr = ep->rbase;
	ep->tbase = PADDR(e->tdr);
	ep->tbptr = ep->tbase;
	eieio();
}


#endif
.
## diffname mpc/spi.c 2000/0521
## diff -e /n/emeliedump/2000/0516/sys/src/9/mpc/spi.c /n/emeliedump/2000/0521/sys/src/9/mpc/spi.c
246c
print("%d status = %lux len=%d\n", ctlr->rdrx, status, len);
.
231c
print("cpcom = %p\n", &ctlr->spi->spcom);
.
191c
	print("ttmp = %lux\n", sp->ttmp);
.
186c
	print("rtmp = %lux\n", sp->rtmp);
.
## diffname mpc/spi.c 2000/0817
## diff -e /n/emeliedump/2000/0521/sys/src/9/mpc/spi.c /n/emeliedump/2000/0817/sys/src/9/mpc/spi.c
521c
	dre = &ctlr->tdr[ctlr->tdrh];
	spicmdsend(b,dre,ctlr,1);

	dre=&ctlr->rdr[ctlr->rdrx];
	len=dre->length;
	dcflush(KADDR(dre->addr),len);
	dre->status=(dre->status&BDWrap)|BDEmpty|BDInt;
	ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre);

	freeb(b);
	delay(15);
	return;
}
.
519a
	b = allocb(2);
	b->wp[0] = 0x07;
	b->wp[1] = 0x80;
	b->wp+=2;
.
502,518c
	ctlr = spictlr;
.
500c
  	BD *dre;
	Block *b;
	Ctlr *ctlr;
	int len;
.
497,498c
void
spieraseall(void)
.
451,494c

	b = allocb(2);
	b->wp[0] = 0x07;
	b->wp[1] = addr&0x7f;
	b->wp+=2;

	dre = &ctlr->tdr[ctlr->tdrh];
	spicmdsend(b,dre,ctlr,1);

	dre=&ctlr->rdr[ctlr->rdrx];
	len=dre->length;
	dcflush(KADDR(dre->addr),len);
	dre->status=(dre->status&BDWrap)|BDEmpty|BDInt;
	ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre);

	freeb(b);
	delay(15);
	return;
.
446,448c
	int len;
.
444a
  	BD *dre;
	Block *b;
.
442,443c
void
spierase(ulong addr)
.
439c
  	BD *dre;
	Block *b;
	Ctlr *ctlr;
	int len;

	ctlr = spictlr;

	dre = &ctlr->tdr[ctlr->tdrh];
	
	b = allocb(4);
	b->wp[0] = 0x04;
	b->wp[1] = 0x40;
	b->wp+=2;
	memmove((uchar*)b->wp,buf,2);
	//b->wp[2]=0xf0;
	//b->wp[3]=0xf0;
	b->wp += 2;

	spicmdsend(b,dre,ctlr,1);

	dre=&ctlr->rdr[ctlr->rdrx];
	len=dre->length;
	dcflush(KADDR(dre->addr),len);
	dre->status=(dre->status&BDWrap)|BDEmpty|BDInt;
	ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre);

	freeb(b);
	delay(15);
	//sleep(15000); //sleep for 15ms 
	print("leave spiwriteall\n");
	return;
.
436,437c
void
spiwriteall(uchar *buf)
.
428,433c
	dre = &ctlr->tdr[ctlr->tdrh];

	b = allocb(4);
	b->wp[0] = 0x05 ;
	b->wp[1] = 0x7f & addr;
	b->wp+=2;
	memmove((uchar*)b->wp,buf,2);
	b->wp += 2;

	spicmdsend(b,dre,ctlr,1);

	dre=&ctlr->rdr[ctlr->rdrx];
	len=dre->length;
	dcflush(KADDR(dre->addr),len);
	dre->status=(dre->status&BDWrap)|BDEmpty|BDInt;
	ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre);

	freeb(b);
	delay(15);
	//sleep(15); //sleep for 15ms 
	return;
.
426c
  	BD *dre;
	Block *b;
	Ctlr *ctlr;
	int len;
  
	ctlr = spictlr;
.
423,424c
void
spiwrite(ulong addr, uchar *buf)
.
417,420c
	ctlr = spictlr;

	dre = &ctlr->tdr[ctlr->tdrh];

	b = allocb(4);
	b->wp[0] = 0x80;
	b->wp[1] = 0x00;
	b->wp[2] = 0x00;
	b->wp[3] = 0x00;
	b->wp += 4;

	spicmdsend(b,dre,ctlr,1);

	dre=&ctlr->rdr[ctlr->rdrx];
	len=dre->length;
	dcflush(KADDR(dre->addr),len);
	dre->status=(dre->status&BDWrap)|BDEmpty|BDInt;
	ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre);

	freeb(b);
	microdelay(5);
	return;
.
415c
  	BD *dre;
	Block *b;
	Ctlr *ctlr;
	int len;
.
411,413c
void
spidiswrite(void)
.
385,408c
	dre=&ctlr->rdr[ctlr->rdrx];
	len=dre->length;
	dcflush(KADDR(dre->addr),len);
	dre->status=(dre->status&BDWrap)|BDEmpty|BDInt;
	ctlr->rdrx=NEXT(ctlr->rdrx,Nrdre);

	freeb(b);
	microdelay(5);
	return;
.
378,383c
	dre = &ctlr->tdr[ctlr->tdrh];
	spicmdsend(b,dre,ctlr,1);
.
370,376c
	b = allocb(4);
	b->wp[0] = 0x98;
	b->wp[1] = 0x00;
	b->wp[2] = 0x00;
	b->wp[3] = 0x00;
	b->wp += 4;
.
347,368c
	ctlr = spictlr;
.
345a
	Ctlr *ctlr;
	int len;
.
343,344c
  	BD *dre;
.
340,341c
void
spienwrite(void)
.
332,337c
	b = iallocb(len);
	memmove(b->wp, KADDR(dre->addr), len);
	b->wp += len;


/* get the data out of the receiving block
   the data begins at the 13th bit, and ends at the (13+16)th bit
*/
	buf[0]=b->rp[1]<<4 | b->rp[2]>>4;
	buf[1]=b->rp[2]<<4 | b->rp[3]>>4;

//    print("read out %ux %ux\n", buf[0],buf[1]);
	dcflush(KADDR(dre->addr), len);
	dre->status = (status & BDWrap) | BDEmpty | BDInt;
	ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
	freeb(b);
	m->iomem->pbdat &= ~(IBIT(31));
	microdelay(5);

	return;
.
295,330c
	dre = &ctlr->rdr[ctlr->rdrx];
	status = dre->status;
	len = dre->length;
	
	while(dre->status&BDEmpty) microdelay(10);
.
290,293c
	b = allocb(4);
	b->wp[0] = 0xc0 | addr>>3;
	b->wp[1] = addr<<5;
	b->wp[2] = 0x00;
	b->wp[3] = 0x00;
	b->wp += 4;

	spicmdsend(b, dre, ctlr, 0);

	//delay(10);
.
282,288c
	dre = &ctlr->tdr[ctlr->tdrh];
.
280c
	int len;
	Ctlr *ctlr;
	ushort status;
  
	ctlr = spictlr;
.
278c

  	BD *dre;
.
275,276c

void
spiread(ulong addr,uchar * buf)
.
268,272c
	if(dre->status & BDReady)
		panic("spicmdsend: txstart");
	
	len = BLEN(b);
	dcflush(b->rp, len);

	ctlr->txb[ctlr->tdrh] = b;
	dre->addr = PADDR(b->rp);
	dre->length = len;
	dre->status = (dre->status & BDWrap) | BDReady|BDInt|BDLast;
	m->iomem->pbdat |= IBIT(31);

	ctlr->spi->spcom = 1<<7;	/* transmit now */

	//eieio();
	ctlr->ntq++;
	ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);

	delay(1);

	while(dre->status&BDReady) microdelay(10);

	if(CSdown) m->iomem->pbdat &= ~(IBIT(31));

.
266c
void
spicmdsend(Block *b, BD *dre, Ctlr *ctlr, int CSdown)
{   
	int len;
.
263c
//	spitest();
.
250a

/* get the data out of the receiving block
   the data begins at the 13th bit, and ends at the (13+16)th bit
*/
	buf[0]=b->rp[1]<<4 | b->rp[2]>>4;
	buf[1]=b->rp[2]<<4 | b->rp[3]>>4;

print("the value of address 0 in EEPROM is %ux %ux\n",buf[0],buf[1]);
.
226d
201a
	uchar buf[2];
.
171c
//	print("SPI#%x\n", events);
.
157c

.
## diffname mpc/spi.c 2001/0527 # deleted
## diff -e /n/emeliedump/2000/0817/sys/src/9/mpc/spi.c /n/emeliedump/2001/0527/sys/src/9/mpc/spi.c
1,547d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.