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