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