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