xref: /csrg-svn/sys/vax/if/if_acc.c (revision 34273)
1 /*
2  * Copyright (c) 1982,1986,1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  *
12  *	@(#)if_acc.c	7.4 (Berkeley) 05/14/88
13  */
14 
15 #include "acc.h"
16 #if NACC > 0
17 
18 /*
19  * ACC LH/DH ARPAnet IMP interface driver.
20  */
21 #include "../machine/pte.h"
22 
23 #include "param.h"
24 #include "systm.h"
25 #include "mbuf.h"
26 #include "buf.h"
27 #include "protosw.h"
28 #include "socket.h"
29 #include "vmmac.h"
30 
31 #include "../net/if.h"
32 #include "../netimp/if_imp.h"
33 
34 #include "../vax/cpu.h"
35 #include "../vax/mtpr.h"
36 #include "if_accreg.h"
37 #include "if_uba.h"
38 #include "../vaxuba/ubareg.h"
39 #include "../vaxuba/ubavar.h"
40 
41 int     accprobe(), accattach(), accrint(), accxint();
42 struct  uba_device *accinfo[NACC];
43 u_short accstd[] = { 0 };
44 struct  uba_driver accdriver =
45 	{ accprobe, 0, accattach, 0, accstd, "acc", accinfo };
46 
47 int	accinit(), accoutput(), accdown(), accreset();
48 
49 /*
50  * "Lower half" of IMP interface driver.
51  *
52  * Each IMP interface is handled by a common module which handles
53  * the IMP-host protocol and a hardware driver which manages the
54  * hardware specific details of talking with the IMP.
55  *
56  * The hardware portion of the IMP driver handles DMA and related
57  * management of UNIBUS resources.  The IMP protocol module interprets
58  * contents of these messages and "controls" the actions of the
59  * hardware module during IMP resets, but not, for instance, during
60  * UNIBUS resets.
61  *
62  * The two modules are coupled at "attach time", and ever after,
63  * through the imp interface structure.  Higher level protocols,
64  * e.g. IP, interact with the IMP driver, rather than the ACC.
65  */
66 struct	acc_softc {
67 	struct	imp_softc *acc_imp;	/* data structure shared with IMP */
68 	struct	ifuba acc_ifuba;	/* UNIBUS resources */
69 	struct	mbuf *acc_iq;		/* input reassembly queue */
70 	short	acc_olen;		/* size of last message sent */
71 	char	acc_flush;		/* flush remainder of message */
72 } acc_softc[NACC];
73 
74 /*
75  * Reset the IMP and cause a transmitter interrupt by
76  * performing a null DMA.
77  */
78 accprobe(reg)
79 	caddr_t reg;
80 {
81 	register int br, cvec;		/* r11, r10 value-result */
82 	register struct accdevice *addr = (struct accdevice *)reg;
83 
84 #ifdef lint
85 	br = 0; cvec = br; br = cvec;
86 	accrint(0); accxint(0);
87 #endif
88 	addr->icsr = ACC_RESET; DELAY(5000);
89 	addr->ocsr = ACC_RESET; DELAY(5000);
90 	addr->ocsr = OUT_BBACK; DELAY(5000);
91 	addr->owc = 0;
92 	addr->ocsr = ACC_IE | ACC_GO; DELAY(5000);
93 	addr->ocsr = 0;
94 	if (cvec && cvec != 0x200)	/* transmit -> receive */
95 		cvec -= 4;
96 	return (1);
97 }
98 
99 /*
100  * Call the IMP module to allow it to set up its internal
101  * state, then tie the two modules together by setting up
102  * the back pointers to common data structures.
103  */
104 accattach(ui)
105 	register struct uba_device *ui;
106 {
107 	register struct acc_softc *sc = &acc_softc[ui->ui_unit];
108 	register struct impcb *ip;
109 
110 	if ((sc->acc_imp = impattach(ui->ui_driver->ud_dname, ui->ui_unit,
111 	    accreset)) == 0)
112 		return;
113 	ip = &sc->acc_imp->imp_cb;
114 	ip->ic_init = accinit;
115 	ip->ic_output = accoutput;
116 	ip->ic_down = accdown;
117 	sc->acc_ifuba.ifu_flags = UBA_CANTWAIT;
118 #ifdef notdef
119 	sc->acc_ifuba.ifu_flags |= UBA_NEEDBDP;
120 #endif
121 }
122 
123 /*
124  * Reset interface after UNIBUS reset.
125  * If interface is on specified uba, reset its state.
126  */
127 accreset(unit, uban)
128 	int unit, uban;
129 {
130 	register struct uba_device *ui;
131 	struct acc_softc *sc;
132 
133 	if (unit >= NACC || (ui = accinfo[unit]) == 0 || ui->ui_alive == 0 ||
134 	    ui->ui_ubanum != uban)
135 		return;
136 	printf(" acc%d", unit);
137 	sc = &acc_softc[unit];
138 	sc->acc_imp->imp_if.if_flags &= ~IFF_RUNNING;
139 	accoflush(unit);
140 	/* must go through IMP to allow it to set state */
141 	(*sc->acc_imp->imp_if.if_init)(sc->acc_imp->imp_if.if_unit);
142 }
143 
144 /*
145  * Initialize interface: clear recorded pending operations,
146  * and retrieve, and initialize UNIBUS resources.  Note
147  * return value is used by IMP init routine to mark IMP
148  * unavailable for outgoing traffic.
149  */
150 accinit(unit)
151 	int unit;
152 {
153 	register struct acc_softc *sc;
154 	register struct uba_device *ui;
155 	register struct accdevice *addr;
156 	int info;
157 
158 	if (unit >= NACC || (ui = accinfo[unit]) == 0 || ui->ui_alive == 0) {
159 		printf("acc%d: not alive\n", unit);
160 		return (0);
161 	}
162 	sc = &acc_softc[unit];
163 	/*
164 	 * Header length is 0 since we have to passs
165 	 * the IMP leader up to the protocol interpretation
166 	 * routines.  If we had the header length as
167 	 * sizeof(struct imp_leader), then the if_ routines
168 	 * would asssume we handle it on input and output.
169 	 */
170 	if ((sc->acc_imp->imp_if.if_flags & IFF_RUNNING) == 0 &&
171 	    if_ubainit(&sc->acc_ifuba, ui->ui_ubanum, 0,
172 	     (int)btoc(IMP_RCVBUF)) == 0) {
173 		printf("acc%d: can't initialize\n", unit);
174 		sc->acc_imp->imp_if.if_flags &= ~(IFF_UP | IFF_RUNNING);
175 		return (0);
176 	}
177 	sc->acc_imp->imp_if.if_flags |= IFF_RUNNING;
178 	addr = (struct accdevice *)ui->ui_addr;
179 
180 	/*
181 	 * Reset the imp interface;
182 	 * the delays are pure guesswork.
183 	 */
184         addr->ocsr = ACC_RESET; DELAY(5000);
185 	addr->ocsr = OUT_BBACK;	DELAY(5000);	/* reset host master ready */
186 	addr->ocsr = 0;
187 	if (accinputreset(addr, unit) == 0) {
188 		ui->ui_alive = 0;
189 		return (0);
190 	}
191 
192 	/*
193 	 * Put up a read.  We can't restart any outstanding writes
194 	 * until we're back in synch with the IMP (i.e. we've flushed
195 	 * the NOOPs it throws at us).
196 	 * Note: IMP_RCVBUF includes the leader.
197 	 */
198 	info = sc->acc_ifuba.ifu_r.ifrw_info;
199 	addr->iba = (u_short)info;
200 	addr->iwc = -((IMP_RCVBUF) >> 1);
201 #ifdef LOOPBACK
202 	addr->ocsr |= OUT_BBACK;
203 #endif
204 	addr->icsr =
205 		IN_MRDY | ACC_IE | IN_WEN | ((info & 0x30000) >> 12) | ACC_GO;
206 	return (1);
207 }
208 
209 accinputreset(addr, unit)
210 	register struct accdevice *addr;
211 	register int unit;
212 {
213 	register int i;
214 
215 	addr->icsr = ACC_RESET; DELAY(5000);
216 	addr->icsr = IN_MRDY | IN_WEN;		/* close the relay */
217 	DELAY(10000);
218 	/* YECH!!! */
219 	for (i = 0; i < 500; i++) {
220 		if ((addr->icsr & IN_HRDY) ||
221 		    (addr->icsr & (IN_RMR | IN_IMPBSY)) == 0)
222 			return (1);
223 		addr->icsr = IN_MRDY | IN_WEN; DELAY(10000);
224 		/* keep turning IN_RMR off */
225 	}
226 	printf("acc%d: imp doesn't respond, icsr=%b\n", unit,
227 		addr->icsr, ACC_INBITS);
228 	return (0);
229 }
230 
231 /*
232  * Drop the host ready line to mark host down.
233  */
234 accdown(unit)
235 	int unit;
236 {
237 	register struct accdevice *addr;
238 
239 	addr = (struct accdevice *)(accinfo[unit]->ui_addr);
240         addr->ocsr = ACC_RESET;
241 	DELAY(5000);
242 	addr->ocsr = OUT_BBACK;		/* reset host master ready */
243 	accoflush(unit);
244 	return (1);
245 }
246 
247 accoflush(unit)
248 	int unit;
249 {
250 	register struct acc_softc *sc = &acc_softc[unit];
251 
252 	sc->acc_imp->imp_cb.ic_oactive = 0;
253 	if (sc->acc_ifuba.ifu_xtofree) {
254 		m_freem(sc->acc_ifuba.ifu_xtofree);
255 		sc->acc_ifuba.ifu_xtofree = 0;
256 	}
257 }
258 
259 /*
260  * Start output on an interface.
261  */
262 accoutput(unit, m)
263 	int unit;
264 	struct mbuf *m;
265 {
266 	int info;
267 	register struct acc_softc *sc = &acc_softc[unit];
268 	register struct accdevice *addr;
269 	u_short cmd;
270 
271 	sc->acc_olen = if_wubaput(&sc->acc_ifuba, m);
272 	/*
273 	 * Have request mapped to UNIBUS for
274 	 * transmission; start the output.
275 	 */
276 	if (sc->acc_ifuba.ifu_flags & UBA_NEEDBDP)
277 		UBAPURGE(sc->acc_ifuba.ifu_uba, sc->acc_ifuba.ifu_w.ifrw_bdp);
278 	addr = (struct accdevice *)accinfo[unit]->ui_addr;
279 	info = sc->acc_ifuba.ifu_w.ifrw_info;
280 	addr->oba = (u_short)info;
281 	addr->owc = -((sc->acc_olen + 1) >> 1);
282 	cmd = ACC_IE | OUT_ENLB | ((info & 0x30000) >> 12) | ACC_GO;
283 #ifdef LOOPBACK
284 	cmd |= OUT_BBACK;
285 #endif
286 	addr->ocsr = cmd;
287 	sc->acc_imp->imp_cb.ic_oactive = 1;
288 }
289 
290 /*
291  * Output interrupt handler.
292  */
293 accxint(unit)
294 	int unit;
295 {
296 	register struct acc_softc *sc = &acc_softc[unit];
297 	register struct accdevice *addr;
298 
299 	addr = (struct accdevice *)accinfo[unit]->ui_addr;
300 	if (sc->acc_imp->imp_cb.ic_oactive == 0) {
301 		printf("acc%d: stray xmit interrupt, csr=%b\n", unit,
302 			addr->ocsr, ACC_OUTBITS);
303 		return;
304 	}
305 	sc->acc_imp->imp_if.if_opackets++;
306 	sc->acc_imp->imp_cb.ic_oactive = 0;
307 	if (addr->ocsr & ACC_ERR) {
308 		printf("acc%d: output error, ocsr=%b, icsr=%b\n", unit,
309 			addr->ocsr, ACC_OUTBITS, addr->icsr, ACC_INBITS);
310 		sc->acc_imp->imp_if.if_oerrors++;
311 	}
312 	if (sc->acc_ifuba.ifu_xtofree) {
313 		m_freem(sc->acc_ifuba.ifu_xtofree);
314 		sc->acc_ifuba.ifu_xtofree = 0;
315 	}
316 	impstart(sc->acc_imp);
317 }
318 
319 /*
320  * Input interrupt handler
321  */
322 accrint(unit)
323 	int unit;
324 {
325 	register struct acc_softc *sc = &acc_softc[unit];
326 	register struct accdevice *addr;
327     	struct mbuf *m;
328 	int len, info;
329 
330 	addr = (struct accdevice *)accinfo[unit]->ui_addr;
331 	sc->acc_imp->imp_if.if_ipackets++;
332 
333 	/*
334 	 * Purge BDP; flush message if error indicated.
335 	 */
336 	if (sc->acc_ifuba.ifu_flags & UBA_NEEDBDP)
337 		UBAPURGE(sc->acc_ifuba.ifu_uba, sc->acc_ifuba.ifu_r.ifrw_bdp);
338 	if (addr->icsr & ACC_ERR) {
339 		printf("acc%d: input error, csr=%b\n", unit,
340 		    addr->icsr, ACC_INBITS);
341 		sc->acc_imp->imp_if.if_ierrors++;
342 		sc->acc_flush = 1;
343 	}
344 
345 	if (sc->acc_flush) {
346 		if (addr->icsr & IN_EOM)
347 			sc->acc_flush = 0;
348 		goto setup;
349 	}
350 	len = IMP_RCVBUF + (addr->iwc << 1);
351 	if (len < 0 || len > IMP_RCVBUF) {
352 		printf("acc%d: bad length=%d\n", unit, len);
353 		sc->acc_imp->imp_if.if_ierrors++;
354 		goto setup;
355 	}
356 
357 	/*
358 	 * The offset parameter is always 0 since using
359 	 * trailers on the ARPAnet is insane.
360 	 */
361 	m = if_rubaget(&sc->acc_ifuba, len, 0, &sc->acc_imp->imp_if);
362 	if (m == 0)
363 		goto setup;
364 	if ((addr->icsr & IN_EOM) == 0) {
365 		if (sc->acc_iq)
366 			m_cat(sc->acc_iq, m);
367 		else
368 			sc->acc_iq = m;
369 		goto setup;
370 	}
371 	if (sc->acc_iq) {
372 		m_cat(sc->acc_iq, m);
373 		m = sc->acc_iq;
374 		sc->acc_iq = 0;
375 	}
376 	impinput(unit, m);
377 
378 setup:
379 	/*
380 	 * Setup for next message.
381 	 */
382 	info = sc->acc_ifuba.ifu_r.ifrw_info;
383 	addr->iba = (u_short)info;
384 	addr->iwc = -((IMP_RCVBUF)>> 1);
385 	addr->icsr =
386 		IN_MRDY | ACC_IE | IN_WEN | ((info & 0x30000) >> 12) | ACC_GO;
387 }
388 #endif
389