xref: /inferno-os/os/mpc/spi.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1*74a4d8c2SCharles.Forsyth #include "u.h"
2*74a4d8c2SCharles.Forsyth #include "../port/lib.h"
3*74a4d8c2SCharles.Forsyth #include "mem.h"
4*74a4d8c2SCharles.Forsyth #include "dat.h"
5*74a4d8c2SCharles.Forsyth #include "fns.h"
6*74a4d8c2SCharles.Forsyth #include "io.h"
7*74a4d8c2SCharles.Forsyth #include "../port/error.h"
8*74a4d8c2SCharles.Forsyth 
9*74a4d8c2SCharles.Forsyth /*
10*74a4d8c2SCharles.Forsyth  * basic read/write interface to mpc8xx Serial Peripheral Interface;
11*74a4d8c2SCharles.Forsyth  * used by devtouch.c and devspi.c
12*74a4d8c2SCharles.Forsyth  */
13*74a4d8c2SCharles.Forsyth 
14*74a4d8c2SCharles.Forsyth typedef struct Ctlr Ctlr;
15*74a4d8c2SCharles.Forsyth 
16*74a4d8c2SCharles.Forsyth enum {
17*74a4d8c2SCharles.Forsyth 	/* spi-specific BD flags */
18*74a4d8c2SCharles.Forsyth 	BDContin=	1<<9,	/* continuous mode */
19*74a4d8c2SCharles.Forsyth 	RxeOV=		1<<1,	/* overrun */
20*74a4d8c2SCharles.Forsyth 	TxeUN=		1<<1,	/* underflow */
21*74a4d8c2SCharles.Forsyth 	BDme=		1<<0,	/* multimaster error */
22*74a4d8c2SCharles.Forsyth 	BDrxerr=		RxeOV|BDme,
23*74a4d8c2SCharles.Forsyth 	BDtxerr=		TxeUN|BDme,
24*74a4d8c2SCharles.Forsyth 
25*74a4d8c2SCharles.Forsyth 	/* spmod */
26*74a4d8c2SCharles.Forsyth 	MLoop=	1<<14,	/* loopback mode */
27*74a4d8c2SCharles.Forsyth 	MClockInv= 1<<13,	/* inactive state of SPICLK is high */
28*74a4d8c2SCharles.Forsyth 	MClockPhs= 1<<12,	/* SPCLK starts toggling at beginning of transfer */
29*74a4d8c2SCharles.Forsyth 	MDiv16=	1<<11,	/* use BRGCLK/16 as input to SPI baud rate */
30*74a4d8c2SCharles.Forsyth 	MRev=	1<<10,	/* normal operation */
31*74a4d8c2SCharles.Forsyth 	MMaster=	1<<9,
32*74a4d8c2SCharles.Forsyth 	MSlave=	0<<9,
33*74a4d8c2SCharles.Forsyth 	MEnable=	1<<8,
34*74a4d8c2SCharles.Forsyth 	/* LEN, PS fields */
35*74a4d8c2SCharles.Forsyth 
36*74a4d8c2SCharles.Forsyth 	/* spcom */
37*74a4d8c2SCharles.Forsyth 	STR=		1<<7,	/* start transmit */
38*74a4d8c2SCharles.Forsyth 
39*74a4d8c2SCharles.Forsyth 	/* spie */
40*74a4d8c2SCharles.Forsyth 	MME =	1<<5,
41*74a4d8c2SCharles.Forsyth 	TXE =	1<<4,
42*74a4d8c2SCharles.Forsyth 	BSY =	1<<2,
43*74a4d8c2SCharles.Forsyth 	TXB =	1<<1,
44*74a4d8c2SCharles.Forsyth 	RXB =	1<<0,
45*74a4d8c2SCharles.Forsyth 
46*74a4d8c2SCharles.Forsyth 	/* port B bits */
47*74a4d8c2SCharles.Forsyth 	SPIMISO =	IBIT(28),	/* master mode input */
48*74a4d8c2SCharles.Forsyth 	SPIMOSI = IBIT(29),	/* master mode output */
49*74a4d8c2SCharles.Forsyth 	SPICLK = IBIT(30),
50*74a4d8c2SCharles.Forsyth 
51*74a4d8c2SCharles.Forsyth 	/* maximum SPI I/O (can change) */
52*74a4d8c2SCharles.Forsyth 	Bufsize =	64,
53*74a4d8c2SCharles.Forsyth };
54*74a4d8c2SCharles.Forsyth 
55*74a4d8c2SCharles.Forsyth /*
56*74a4d8c2SCharles.Forsyth  * SPI software structures
57*74a4d8c2SCharles.Forsyth  */
58*74a4d8c2SCharles.Forsyth 
59*74a4d8c2SCharles.Forsyth struct Ctlr {
60*74a4d8c2SCharles.Forsyth 	Lock;
61*74a4d8c2SCharles.Forsyth 	QLock	io;
62*74a4d8c2SCharles.Forsyth 	int	init;
63*74a4d8c2SCharles.Forsyth 	SPI*	spi;
64*74a4d8c2SCharles.Forsyth 	IOCparam*	sp;
65*74a4d8c2SCharles.Forsyth 
66*74a4d8c2SCharles.Forsyth 	BD*	rd;
67*74a4d8c2SCharles.Forsyth 	BD*	td;
68*74a4d8c2SCharles.Forsyth 	int	phase;
69*74a4d8c2SCharles.Forsyth 	Rendez	r;
70*74a4d8c2SCharles.Forsyth 	char*	txbuf;
71*74a4d8c2SCharles.Forsyth 	char*	rxbuf;
72*74a4d8c2SCharles.Forsyth };
73*74a4d8c2SCharles.Forsyth 
74*74a4d8c2SCharles.Forsyth static	Ctlr	spictlr[1];
75*74a4d8c2SCharles.Forsyth 
76*74a4d8c2SCharles.Forsyth /* dcflush isn't needed if rxbuf and txbuf allocated in uncached IMMR memory */
77*74a4d8c2SCharles.Forsyth #define	DCFLUSH(a,n)
78*74a4d8c2SCharles.Forsyth 
79*74a4d8c2SCharles.Forsyth static	void	interrupt(Ureg*, void*);
80*74a4d8c2SCharles.Forsyth 
81*74a4d8c2SCharles.Forsyth /*
82*74a4d8c2SCharles.Forsyth  * called by the reset routine of any driver using the SPI
83*74a4d8c2SCharles.Forsyth  */
84*74a4d8c2SCharles.Forsyth void
spireset(void)85*74a4d8c2SCharles.Forsyth spireset(void)
86*74a4d8c2SCharles.Forsyth {
87*74a4d8c2SCharles.Forsyth 	IMM *io;
88*74a4d8c2SCharles.Forsyth 	SPI *spi;
89*74a4d8c2SCharles.Forsyth 	IOCparam *sp;
90*74a4d8c2SCharles.Forsyth 	CPMdev *cpm;
91*74a4d8c2SCharles.Forsyth 	Ctlr *ctlr;
92*74a4d8c2SCharles.Forsyth 
93*74a4d8c2SCharles.Forsyth 	ctlr = spictlr;
94*74a4d8c2SCharles.Forsyth 	if(ctlr->init)
95*74a4d8c2SCharles.Forsyth 		return;
96*74a4d8c2SCharles.Forsyth 	ctlr->init = 1;
97*74a4d8c2SCharles.Forsyth 	cpm = cpmdev(CPspi);
98*74a4d8c2SCharles.Forsyth 	spi = cpm->regs;
99*74a4d8c2SCharles.Forsyth 	ctlr->spi = spi;
100*74a4d8c2SCharles.Forsyth 	sp = cpm->param;
101*74a4d8c2SCharles.Forsyth 	if(sp == nil){
102*74a4d8c2SCharles.Forsyth 		print("SPI: can't allocate new parameter memory\n");
103*74a4d8c2SCharles.Forsyth 		return;
104*74a4d8c2SCharles.Forsyth 	}
105*74a4d8c2SCharles.Forsyth 	ctlr->sp = sp;
106*74a4d8c2SCharles.Forsyth 
107*74a4d8c2SCharles.Forsyth 	if(ctlr->rxbuf == nil)
108*74a4d8c2SCharles.Forsyth 		ctlr->rxbuf = cpmalloc(Bufsize, 2);
109*74a4d8c2SCharles.Forsyth 	if(ctlr->txbuf == nil)
110*74a4d8c2SCharles.Forsyth 		ctlr->txbuf = cpmalloc(Bufsize, 2);
111*74a4d8c2SCharles.Forsyth 
112*74a4d8c2SCharles.Forsyth 	if(ctlr->rd == nil){
113*74a4d8c2SCharles.Forsyth 		ctlr->rd = bdalloc(1);
114*74a4d8c2SCharles.Forsyth 		ctlr->rd->addr = PADDR(ctlr->rxbuf);
115*74a4d8c2SCharles.Forsyth 		ctlr->rd->length = 0;
116*74a4d8c2SCharles.Forsyth 		ctlr->rd->status = BDWrap;
117*74a4d8c2SCharles.Forsyth 	}
118*74a4d8c2SCharles.Forsyth 	if(ctlr->td == nil){
119*74a4d8c2SCharles.Forsyth 		ctlr->td = bdalloc(1);
120*74a4d8c2SCharles.Forsyth 		ctlr->td->addr = PADDR(ctlr->txbuf);
121*74a4d8c2SCharles.Forsyth 		ctlr->td->length = 0;
122*74a4d8c2SCharles.Forsyth 		ctlr->td->status = BDWrap|BDLast;
123*74a4d8c2SCharles.Forsyth 	}
124*74a4d8c2SCharles.Forsyth 
125*74a4d8c2SCharles.Forsyth 	/* select port pins */
126*74a4d8c2SCharles.Forsyth 	io = ioplock();
127*74a4d8c2SCharles.Forsyth 	io->pbdir |= SPICLK | SPIMOSI | SPIMISO;
128*74a4d8c2SCharles.Forsyth 	io->pbpar |= SPICLK | SPIMOSI | SPIMISO;
129*74a4d8c2SCharles.Forsyth 	iopunlock();
130*74a4d8c2SCharles.Forsyth 
131*74a4d8c2SCharles.Forsyth 	/* explicitly initialise parameters, because InitRxTx can't be used (see i2c/spi relocation errata) */
132*74a4d8c2SCharles.Forsyth 	sp = ctlr->sp;
133*74a4d8c2SCharles.Forsyth 	sp->rbase = PADDR(ctlr->rd);
134*74a4d8c2SCharles.Forsyth 	sp->tbase = PADDR(ctlr->td);
135*74a4d8c2SCharles.Forsyth 	sp->rfcr = 0x18;
136*74a4d8c2SCharles.Forsyth 	sp->tfcr = 0x18;
137*74a4d8c2SCharles.Forsyth 	sp->mrblr = Bufsize;
138*74a4d8c2SCharles.Forsyth 	sp->rstate = 0;
139*74a4d8c2SCharles.Forsyth 	sp->rptr = 0;
140*74a4d8c2SCharles.Forsyth 	sp->rbptr = sp->rbase;
141*74a4d8c2SCharles.Forsyth 	sp->rcnt = 0;
142*74a4d8c2SCharles.Forsyth 	sp->tstate = 0;
143*74a4d8c2SCharles.Forsyth 	sp->tbptr = sp->tbase;
144*74a4d8c2SCharles.Forsyth 	sp->tptr = 0;
145*74a4d8c2SCharles.Forsyth 	sp->tcnt = 0;
146*74a4d8c2SCharles.Forsyth 	eieio();
147*74a4d8c2SCharles.Forsyth 
148*74a4d8c2SCharles.Forsyth 	spi->spmode = MDiv16 | MRev | MMaster | ((8-1)<<4) | 1;	/* 8 bit characters */
149*74a4d8c2SCharles.Forsyth 	if(0)
150*74a4d8c2SCharles.Forsyth 		spi->spmode |= MLoop;	/* internal loop back mode for testing */
151*74a4d8c2SCharles.Forsyth 
152*74a4d8c2SCharles.Forsyth 	spi->spie = ~0;	/* clear events */
153*74a4d8c2SCharles.Forsyth 	eieio();
154*74a4d8c2SCharles.Forsyth 	spi->spim = MME|TXE|BSY|TXB|RXB;
155*74a4d8c2SCharles.Forsyth 	eieio();
156*74a4d8c2SCharles.Forsyth 	spi->spmode |= MEnable;
157*74a4d8c2SCharles.Forsyth 
158*74a4d8c2SCharles.Forsyth 	intrenable(VectorCPIC+cpm->irq, interrupt, spictlr, BUSUNKNOWN, "spi");
159*74a4d8c2SCharles.Forsyth 
160*74a4d8c2SCharles.Forsyth }
161*74a4d8c2SCharles.Forsyth 
162*74a4d8c2SCharles.Forsyth enum {
163*74a4d8c2SCharles.Forsyth 	Idling,
164*74a4d8c2SCharles.Forsyth 	Waitval,
165*74a4d8c2SCharles.Forsyth 	Readyval
166*74a4d8c2SCharles.Forsyth };
167*74a4d8c2SCharles.Forsyth 
168*74a4d8c2SCharles.Forsyth static void
interrupt(Ureg *,void * arg)169*74a4d8c2SCharles.Forsyth interrupt(Ureg*, void *arg)
170*74a4d8c2SCharles.Forsyth {
171*74a4d8c2SCharles.Forsyth 	int events;
172*74a4d8c2SCharles.Forsyth 	Ctlr *ctlr;
173*74a4d8c2SCharles.Forsyth 	SPI *spi;
174*74a4d8c2SCharles.Forsyth 
175*74a4d8c2SCharles.Forsyth 	ctlr = arg;
176*74a4d8c2SCharles.Forsyth 	spi = ctlr->spi;
177*74a4d8c2SCharles.Forsyth 	events = spi->spie;
178*74a4d8c2SCharles.Forsyth 	eieio();
179*74a4d8c2SCharles.Forsyth 	spi->spie = events;
180*74a4d8c2SCharles.Forsyth 	if(events & (MME|BSY|TXE)){
181*74a4d8c2SCharles.Forsyth 		print("SPI#%x\n", events);
182*74a4d8c2SCharles.Forsyth 		if(ctlr->phase != Idling){
183*74a4d8c2SCharles.Forsyth 			ctlr->phase = Idling;
184*74a4d8c2SCharles.Forsyth 			wakeup(&ctlr->r);
185*74a4d8c2SCharles.Forsyth 		}
186*74a4d8c2SCharles.Forsyth 	}else if(events & RXB){
187*74a4d8c2SCharles.Forsyth 		if(ctlr->phase == Waitval){
188*74a4d8c2SCharles.Forsyth 			ctlr->phase = Readyval;
189*74a4d8c2SCharles.Forsyth 			wakeup(&ctlr->r);
190*74a4d8c2SCharles.Forsyth 		}
191*74a4d8c2SCharles.Forsyth 	}
192*74a4d8c2SCharles.Forsyth }
193*74a4d8c2SCharles.Forsyth 
194*74a4d8c2SCharles.Forsyth static int
done(void * a)195*74a4d8c2SCharles.Forsyth done(void *a)
196*74a4d8c2SCharles.Forsyth {
197*74a4d8c2SCharles.Forsyth 	return ((Ctlr*)a)->phase != Waitval;
198*74a4d8c2SCharles.Forsyth }
199*74a4d8c2SCharles.Forsyth 
200*74a4d8c2SCharles.Forsyth /*
201*74a4d8c2SCharles.Forsyth  * send `nout' bytes on SPI from `out' and read as many bytes of reply into buffer `in';
202*74a4d8c2SCharles.Forsyth  * return the number of bytes received, or -1 on error.
203*74a4d8c2SCharles.Forsyth  */
204*74a4d8c2SCharles.Forsyth long
spioutin(void * out,long nout,void * in)205*74a4d8c2SCharles.Forsyth spioutin(void *out, long nout, void *in)
206*74a4d8c2SCharles.Forsyth {
207*74a4d8c2SCharles.Forsyth 	Ctlr *ctlr;
208*74a4d8c2SCharles.Forsyth 	int nb, p;
209*74a4d8c2SCharles.Forsyth 
210*74a4d8c2SCharles.Forsyth 	ctlr = spictlr;
211*74a4d8c2SCharles.Forsyth 	if(nout > Bufsize)
212*74a4d8c2SCharles.Forsyth 		return -1;
213*74a4d8c2SCharles.Forsyth 	qlock(&ctlr->io);
214*74a4d8c2SCharles.Forsyth 	if(waserror()){
215*74a4d8c2SCharles.Forsyth 		qunlock(&ctlr->io);
216*74a4d8c2SCharles.Forsyth 		return -1;
217*74a4d8c2SCharles.Forsyth 	}
218*74a4d8c2SCharles.Forsyth 	if(ctlr->phase != Idling)
219*74a4d8c2SCharles.Forsyth 		sleep(&ctlr->r, done, ctlr);
220*74a4d8c2SCharles.Forsyth 	memmove(ctlr->txbuf, out, nout);
221*74a4d8c2SCharles.Forsyth 	DCFLUSH(ctlr->txbuf, Bufsize);
222*74a4d8c2SCharles.Forsyth 	DCFLUSH(ctlr->rxbuf, Bufsize);
223*74a4d8c2SCharles.Forsyth 	ilock(ctlr);
224*74a4d8c2SCharles.Forsyth 	ctlr->phase = Waitval;
225*74a4d8c2SCharles.Forsyth 	ctlr->td->length = nout;
226*74a4d8c2SCharles.Forsyth 	ctlr->rd->status = BDEmpty|BDWrap|BDInt;
227*74a4d8c2SCharles.Forsyth 	ctlr->td->status = BDReady|BDWrap|BDLast;
228*74a4d8c2SCharles.Forsyth 	eieio();
229*74a4d8c2SCharles.Forsyth 	ctlr->spi->spcom = STR;
230*74a4d8c2SCharles.Forsyth 	eieio();
231*74a4d8c2SCharles.Forsyth 	iunlock(ctlr);
232*74a4d8c2SCharles.Forsyth 	sleep(&ctlr->r, done, ctlr);
233*74a4d8c2SCharles.Forsyth 	nb = ctlr->rd->length;
234*74a4d8c2SCharles.Forsyth 	if(nb > nout)
235*74a4d8c2SCharles.Forsyth 		nb = nout;	/* shouldn't happen */
236*74a4d8c2SCharles.Forsyth 	p = ctlr->phase;
237*74a4d8c2SCharles.Forsyth 	poperror();
238*74a4d8c2SCharles.Forsyth 	qunlock(&ctlr->io);
239*74a4d8c2SCharles.Forsyth 	if(p != Readyval)
240*74a4d8c2SCharles.Forsyth 		return -1;
241*74a4d8c2SCharles.Forsyth 	memmove(in, ctlr->rxbuf, nb);
242*74a4d8c2SCharles.Forsyth 	return nb;
243*74a4d8c2SCharles.Forsyth }
244