1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 #include "io.h"
9 #include "archipe.h"
10
11 enum {
12 FPGASIZE = 8*1024*1024,
13 FPGATMR = 2-1, /* BCLK timer number (mapped to origin 0) */
14 TIMERSH = FPGATMR*4, /* timer field shift */
15
16 COM3= IBIT(1)|IBIT(2), /* sccr: clock output disabled */
17
18 ConfDone = 1<<1,
19 nStatus = 1<<0,
20 };
21
22 /*
23 * provisional FPGA interface for simple development work;
24 * for more complex things, use this to load the device then have a
25 * purpose-built device driver or module
26 */
27
28 enum{
29 Qdir,
30 Qmemb,
31 Qmemw,
32 Qprog,
33 Qctl,
34 Qclk,
35 Qstatus,
36 };
37
38 static struct {
39 QLock;
40 int clkspeed;
41 } fpga;
42
43 static void resetfpga(void);
44 static void startfpga(int);
45 static int endfpga(void);
46 static int fpgastatus(void);
47 static void powerfpga(int);
48 static void vclkenable(int);
49 static void vclkset(char*, char*, char*, char*);
50 static void memmovew(ushort*, ushort*, long);
51
52 static Dirtab fpgadir[]={
53 ".", {Qdir, 0, QTDIR}, 0, 0555,
54 "fpgamemb", {Qmemb, 0}, FPGASIZE, 0666,
55 "fpgamemw", {Qmemw, 0}, FPGASIZE, 0666,
56 "fpgaprog", {Qprog, 0}, 0, 0222,
57 "fpgastatus", {Qstatus, 0}, 0, 0444,
58 "fpgactl", {Qctl, 0}, 0, 0666,
59 "fpgaclk", {Qclk, 0}, 0, 0666,
60 };
61
62 static char Eodd[] = "odd count or offset";
63
64 static void
fpgareset(void)65 fpgareset(void)
66 {
67 powerfpga(0);
68 }
69
70 static Chan*
fpgaattach(char * spec)71 fpgaattach(char *spec)
72 {
73 return devattach('G', spec);
74 }
75
76 static Walkqid*
fpgawalk(Chan * c,Chan * nc,char ** name,int nname)77 fpgawalk(Chan *c, Chan *nc, char **name, int nname)
78 {
79 return devwalk(c, nc, name, nname, fpgadir, nelem(fpgadir), devgen);
80 }
81
82 static int
fpgastat(Chan * c,uchar * dp,int n)83 fpgastat(Chan *c, uchar *dp, int n)
84 {
85 return devstat(c, dp, n, fpgadir, nelem(fpgadir), devgen);
86 }
87
88 static Chan*
fpgaopen(Chan * c,int omode)89 fpgaopen(Chan *c, int omode)
90 {
91 return devopen(c, omode, fpgadir, nelem(fpgadir), devgen);
92 }
93
94 static void
fpgaclose(Chan *)95 fpgaclose(Chan*)
96 {
97 }
98
99 static long
fpgaread(Chan * c,void * buf,long n,vlong offset)100 fpgaread(Chan *c, void *buf, long n, vlong offset)
101 {
102 int v;
103 char stat[32], *p;
104
105 if(c->qid.type & QTDIR)
106 return devdirread(c, buf, n, fpgadir, nelem(fpgadir), devgen);
107
108 switch((ulong)c->qid.path){
109 case Qmemb:
110 if(offset >= FPGASIZE)
111 return 0;
112 if(offset+n >= FPGASIZE)
113 n = FPGASIZE-offset;
114 memmove(buf, KADDR(FPGAMEM+offset), n);
115 return n;
116 case Qmemw:
117 if((n | offset) & 1)
118 error(Eodd);
119 if(offset >= FPGASIZE)
120 return 0;
121 if(offset+n >= FPGASIZE)
122 n = FPGASIZE-offset;
123 memmovew((ushort*)buf, (ushort*)KADDR(FPGAMEM+offset), n);
124 return n;
125 case Qstatus:
126 v = fpgastatus();
127 p = seprint(stat, stat+sizeof(stat), "%sconfig", v&ConfDone?"":"!");
128 seprint(p, stat+sizeof(stat), " %sstatus\n", v&nStatus?"":"!");
129 return readstr(offset, buf, n, stat);
130 case Qclk:
131 return readnum(offset, buf, n, fpga.clkspeed, NUMSIZE);
132 case Qctl:
133 case Qprog:
134 return 0;
135 }
136 error(Egreg);
137 return 0; /* not reached */
138 }
139
140 static long
fpgawrite(Chan * c,void * buf,long n,vlong offset)141 fpgawrite(Chan *c, void *buf, long n, vlong offset)
142 {
143 int i, j, v;
144 ulong w;
145 Cmdbuf *cb;
146 ulong *cfg;
147 uchar *cp;
148
149 switch((ulong)c->qid.path){
150 case Qmemb:
151 if(offset >= FPGASIZE)
152 return 0;
153 if(offset+n >= FPGASIZE)
154 n = FPGASIZE-offset;
155 memmove(KADDR(FPGAMEM+offset), buf, n);
156 return n;
157 case Qmemw:
158 if((n | offset) & 1)
159 error(Eodd);
160 if(offset >= FPGASIZE)
161 return 0;
162 if(offset+n >= FPGASIZE)
163 n = FPGASIZE-offset;
164 memmovew((ushort*)KADDR(FPGAMEM+offset), (ushort*)buf, n);
165 return n;
166 case Qctl:
167 cb = parsecmd(buf, n);
168 if(waserror()){
169 free(cb);
170 nexterror();
171 }
172 if(cb->nf < 1)
173 error(Ebadarg);
174 if(strcmp(cb->f[0], "reset") == 0)
175 resetfpga();
176 else if(strcmp(cb->f[0], "bclk") == 0){
177 v = 48;
178 if(cb->nf > 1)
179 v = strtoul(cb->f[1], nil, 0);
180 if(v <= 0 || 48%v != 0)
181 error(Ebadarg);
182 startfpga(48/v-1);
183 }else if(strcmp(cb->f[0], "vclk") == 0){
184 if(cb->nf == 5){ /* vclk n m v r */
185 vclkenable(1);
186 vclkset(cb->f[1], cb->f[2], cb->f[3], cb->f[4]);
187 }else
188 vclkenable(cb->nf < 2 || strcmp(cb->f[1], "on") == 0);
189 }else if(strcmp(cb->f[0], "power") == 0)
190 powerfpga(cb->nf < 2 || strcmp(cb->f[1], "off") != 0);
191 else
192 error(Ebadarg);
193 poperror();
194 free(cb);
195 return n;
196 case Qprog:
197 qlock(&fpga);
198 if(waserror()){
199 qunlock(&fpga);
200 nexterror();
201 }
202 powerfpga(1);
203 resetfpga();
204 cfg = KADDR(FPGACR);
205 cp = buf;
206 for(i=0; i<n; i++){
207 w = cp[i];
208 for(j=0; j<8; j++){
209 *cfg = w&1;
210 w >>= 1;
211 }
212 }
213 for(j=0; j<50; j++) /* Altera note says at least 10 clock cycles, but microblaster uses 50 */
214 *cfg = 0;
215 v = fpgastatus();
216 if(v != (nStatus|ConfDone)){
217 snprint(up->genbuf, sizeof(up->genbuf), "error loading fpga: status %d", v);
218 error(up->genbuf);
219 }
220 poperror();
221 qunlock(&fpga);
222 return n;
223 }
224 error(Egreg);
225 return 0; /* not reached */
226 }
227
228 /*
229 * PDN seems to control power to the FPGA subsystem
230 * but it is not documented nor is its scope clear (PLL as well?).
231 * It will not run without it.
232 */
233 static void
powerfpga(int on)234 powerfpga(int on)
235 {
236 IMM *io;
237
238 io = ioplock();
239 if(io->sccr & COM3){
240 io->sccrk = KEEP_ALIVE_KEY;
241 io->sccr &= ~ COM3; /* FPGA designs can use the clock */
242 io->sccrk = ~KEEP_ALIVE_KEY;
243 }
244 io->pcpar &= ~PDN;
245 io->pcdir |= PDN;
246 if(on)
247 io->pcdat &= ~PDN;
248 else
249 io->pcdat |= PDN;
250 iopunlock();
251 }
252
253 static void
resetfpga(void)254 resetfpga(void)
255 {
256 IMM *io;
257
258 io = ioplock();
259 io->pcpar &= ~nCONFIG;
260 io->pcdir |= nCONFIG;
261 io->pcdat &= ~nCONFIG;
262 microdelay(200);
263 io->pcdat |= nCONFIG;
264 iopunlock();
265 }
266
267 static int
fpgastatus(void)268 fpgastatus(void)
269 {
270 /* isolate status bits IP_B0 and IP_B1 */
271 return (m->iomem->pipr>>14) & (ConfDone|nStatus);
272 }
273
274 static void
startfpga(int scale)275 startfpga(int scale)
276 {
277 IMM *io;
278
279 io = ioplock();
280 io->tgcr &= ~(0xF<<TIMERSH);
281 io->tmr2 = ((scale&0xFF)<<8) | 0x2A;
282 io->tcn2 = 0;
283 io->trr2 = 0;
284 io->ter2 = 0xFFFF;
285 io->tgcr |= 0x1<<TIMERSH;
286 io->padir |= BCLK;
287 io->papar |= BCLK;
288 iopunlock();
289 }
290
291 static void
vclkenable(int i)292 vclkenable(int i)
293 {
294 IMM *io;
295
296 io = ioplock();
297 io->padir &= ~VCLK;
298 io->papar &= ~VCLK;
299 io->pbdir |= EnableVCLK;
300 io->pbpar &= ~EnableVCLK;
301 if(i)
302 io->pbdat |= EnableVCLK;
303 else
304 io->pbdat &= ~EnableVCLK;
305 iopunlock();
306 }
307
308 static void
vclkin(ulong * clk,int v)309 vclkin(ulong *clk, int v)
310 {
311 int i;
312
313 for(i=0; i<7; i++)
314 *clk = (v>>i) & 1;
315 }
316
317 static void
vclkset(char * ns,char * ms,char * vs,char * rs)318 vclkset(char *ns, char *ms, char *vs, char *rs)
319 {
320 int n, m, v, r;
321 ulong *clk;
322
323 clk = KADDR(CLOCKCR);
324 n = strtol(ns, nil, 0);
325 m = strtol(ms, nil, 0);
326 v = strtol(vs, nil, 0);
327 r = strtol(rs, nil, 0);
328 if(n < 3 || n > 127 || m < 3 || m > 127 || v != 1 && v != 8 ||
329 r != 1 && r != 2 && r != 4 && r != 8)
330 error(Ebadarg);
331 vclkenable(0);
332 vclkin(clk, n);
333 vclkin(clk, m);
334 *clk = (v==0) & 1;
335 *clk = 1; *clk = 1;
336 *clk = r == 2 || r == 8;
337 *clk = r == 4 || r == 8;
338 *clk = 1; /* clock out */
339 *clk = 0; /* disable clk/x */
340 *clk = 1; *clk = 0; *clk = 1;
341 *clk = 0; *clk = 0; *clk = 0;
342 vclkenable(1);
343 }
344
345 /*
346 * copy data aligned on 16-bit word boundaries.
347 */
348 static void
memmovew(ushort * to,ushort * from,long count)349 memmovew(ushort *to, ushort *from, long count)
350 {
351 int n;
352
353 if(count <= 0)
354 return;
355 count >>= 1;
356 n = (count+7) >> 3;
357 switch(count&7) { /* Duff's device */
358 case 0: do { *to++ = *from++;
359 case 7: *to++ = *from++;
360 case 6: *to++ = *from++;
361 case 5: *to++ = *from++;
362 case 4: *to++ = *from++;
363 case 3: *to++ = *from++;
364 case 2: *to++ = *from++;
365 case 1: *to++ = *from++;
366 } while(--n > 0);
367 }
368 }
369
370 Dev fpgadevtab = {
371 'G',
372 "fpga",
373
374 fpgareset,
375 devinit,
376 devshutdown,
377 fpgaattach,
378 fpgawalk,
379 fpgastat,
380 fpgaopen,
381 devcreate,
382 fpgaclose,
383 fpgaread,
384 devbread,
385 fpgawrite,
386 devbwrite,
387 devremove,
388 devwstat,
389 };
390