xref: /inferno-os/os/ipengine/devfpga.c (revision 556f8a312ed1b20f8ff25c104928646828e8b05c)
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
65 fpgareset(void)
66 {
67 	powerfpga(0);
68 }
69 
70 static Chan*
71 fpgaattach(char *spec)
72 {
73 	return devattach('G', spec);
74 }
75 
76 static Walkqid*
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
83 fpgastat(Chan *c, uchar *dp, int n)
84 {
85 	return devstat(c, dp, n, fpgadir, nelem(fpgadir), devgen);
86 }
87 
88 static Chan*
89 fpgaopen(Chan *c, int omode)
90 {
91 	return devopen(c, omode, fpgadir, nelem(fpgadir), devgen);
92 }
93 
94 static void
95 fpgaclose(Chan*)
96 {
97 }
98 
99 static long
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
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
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
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
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
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
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
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
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
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