xref: /original-bsd/sys/vax/uba/lpa.c (revision 0f89e6eb)
1 /*
2  * Copyright (c) 1982 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  *
6  *	@(#)lpa.c	6.3 (Berkeley) 06/08/85
7  */
8 
9 #include "lpa.h"
10 #if NLPA > 0
11 
12 #include "param.h"
13 #include "dir.h"
14 #include "user.h"
15 #include "buf.h"
16 #include "proc.h"
17 #include "ioctl.h"
18 #include "uio.h"
19 
20 #include "ubavar.h"
21 
22 /*
23  * LPA driver for -- Asa Romberger
24  *
25  *	open
26  *	write microcode
27  *	write dedicated mode dispatch table
28  *	ioctl TIOCSETP to set parameters
29  *		struct iocb {
30  *			short *baddr;	buffer address
31  *			short rate;	- 1,000,000 / frequency in Hz
32  *			short wc;	15-13 = number of buffers - 1
33  *					12-0 = buffer size in words
34  *		} iocb;
35  *	read - 1 character indicating buffer index
36  *		fill or empty buffer
37  * minor device number = DDCCCCCC where:
38  *	DD	= 00 for analog input
39  *		= 01 for analog output
40  *	CCCCCC	= channel number
41  */
42  *	define NOMCODE to eliminate the microcode download check
43  */
44 /* #define TRACELPA */
45 /* #define NOMCODE */
46 
47 #ifdef TRACELPA
48 #	define TRACER(x)	printf(x)
49 #	define TRACERN(x, d)	printf(x, d)
50 #else
51 #	define TRACER(x)
52 #	define TRACERN(x, d)
53 #endif
54 
55 	/* PRIORITY AT WHICH PROGRAM SHOULD RUN */
56 	/* THIS SHOULD EVENTUALLY  TELL UNIX THIS IS A REAL-TIME DEVICE */
57 
58 #define NICE	0
59 
60 #define inc(v)		(sc->v = ((sc->v + 1) % sc->sc_nbuf))
61 
62 #define LPAPRI		(PZERO + 0)
63 #define LPAUNIT(dev)	0
64 #define LPADEVICE(dev)	(((dev) >> 6) & 03)
65 #define LPACHANNEL(dev)	((dev) & 077)
66 
67 int	lpaprobe(), lpaattach(), lpaiintr(), lpaointr();
68 u_short	lpastd[] = {0170460, 0};
69 struct	uba_device *lpadinfo[NLPA];
70 struct uba_driver lpadriver =
71   {lpaprobe, 0, lpaattach, 0, lpastd, "lpa", lpadinfo, 0, 0, 0 };
72 
73 struct lpa_softc {
74 	int	sc_flag;	/* flags, as defined below */
75 	int	sc_device;	/* device: 0 = analog in, 1 = analog out */
76 	int	sc_channel;	/* device channel number */
77 	struct buf sc_ubuffer;	/* user buffer header */
78 	int	sc_ubabuf;	/* uba allocation pointer for buffer */
79 	int	sc_ubufn;	/* present buffer that user is accessing */
80 	int	sc_lbufn;	/* present buffer that lpa is accessing */
81 	int	sc_lbufnx;	/* next buffer for lpa (value in ustat) */
82 	int	sc_nbuf;	/* number of buffers */
83 	int	sc_count;	/* buffer size in words */
84 	short	sc_ustat;	/* user status word */
85 	struct buf sc_ustatbuf;	/* dummy user status word buffer for ubasetup */
86 	int	sc_ubaustat;	/* uba allocation pointer for ustat */
87 	struct buf *sc_buffer;	/* scratch buffer header */
88 	int	sc_start;	/* 0 if lpa operation has been started */
89 } lpa_softc[NLPA];
90 
91 /* flags for sc_flag */
92 #define OPEN	01		/* device is open */
93 #define MCODE	02		/* microcode has been loaded */
94 #define DMDT	04		/* dedicated mode dispatch table loaded */
95 #define STTY	010		/* stty call and device initialized */
96 #define SLEEP	020		/* sleeping */
97 
98 /* bits for ustat */
99 #define DONE	0100000		/* done */
100 #define STOP	0040000		/* stop data transfer */
101 #define NBI	0003400		/* next buffer index */
102 #define LBI	0000003		/* last buffer index */
103 
104 struct lpadevice {
105 	short	lcim;		/* control in and maintenance */
106 	short	lcos;		/* control and status out */
107 	short	lrda;		/* request description array address word */
108 	short	lms;		/* maintenance status */
109 };
110 
111 /* control in and maintenance register bits */
112 #define	READYI	0000200		/* ready in */
113 #define IIE	0000100		/* in interrupt enable */
114 #define RDAEXT	0000014		/* rda address extension */
115 #define RDAEXTOFFSET	2	/* offset of RDAEXT from right side */
116 #define GO	0000001		/* go */
117 #define RUN	0100000		/* run */
118 #define RESET	0040000		/* reset */
119 #define CWRITE	0020000		/* cram write */
120 #define EA	0004000		/* enable arbitration */
121 #define ROMO	0002000		/* rom O */
122 #define ROMI	0001000		/* rom I */
123 #define SMICRO	0000400		/* step microprocessor */
124 
125 /* control and status out register bits */
126 #define READYO	0200		/* ready out */
127 #define OIE	0100		/* out interrupt enable */
128 #define UINDEX	0007		/* user index */
129 #define ERROR	0100000		/* error */
130 #define ESTAT	0060000		/* error status */
131 #define ESCODE	0017400		/* error sub code */
132 #define ECODE	0077400		/* error status + error sub code */
133 #define OVERRUN	0243		/* overrun error */
134 
135 /* LPA COMMAND DESCRIPTION AREA */
136 
137 /* INIT COMMAND */
138 #define INIT	0		/* mode */
139 #define MCVERS	4		/* microcode version */
140 #define ACLOCKA	0170404		/* LPA bus addresses */
141 #define ACLOCKB	0170432
142 #define AAD1	0170400
143 #define AAD2	1		/* 0170440 - DOES NOT EXIST */
144 #define ADA	0170420
145 #define ADIO1	1		/* 0167770 - DOES NOT EXIST */
146 #define ADIO2	1		/* 0167760 - DOES NOT EXIST */
147 #define ADIO3	1		/* 0167750 - DOES NOT EXIST */
148 #define ADIO4	1		/* 0167740 - DOES NOT EXIST */
149 #define ADIO5	1		/* 0167730 - DOES NOT EXIST */
150 
151 /* CLOCK START COMMAND */
152 #define CLOCK	1		/* mode */
153 #define CLOCKA	0<<4		/* clock A */
154 	/* clock status word */
155 #define ENACTR	1		/* enable counter */
156 #define R1M	1<<1		/* 1 MHz rate */
157 #define R100K	2<<1		/* 100 KHz rate */
158 #define R10K	3<<1		/* 10 KHz rate */
159 #define R1K	4<<1		/* 1 KHz rate */
160 #define R100	5<<1		/* 100 Hz rate */
161 #define REXT	6<<1		/* external rate (from st1 input) */
162 #define R60	7<<1		/* line frequency rate */
163 #define MFIE	0100		/* mode flag interrupt enable */
164 #define MSI	0<<8		/* single interval mode */
165 #define MRI	1<<8		/* repeat interval mode */
166 #define MEET	2<<8		/* external event time mode */
167 #define MEETZ	3<<8		/* external event time mode from zero base */
168 #define ST1EC	020000		/* st1 enable counter */
169 #define ST1IE	040000		/* st1 interrupt enable */
170 
171 /* DATA TRANSFER START COMMAND */
172 #define DTS	2		/* mode */
173 #define SCHAN	1<<8		/* single channel */
174 
175 lpaprobe(reg)
176 	caddr_t reg;
177 {
178 	register int br, cvec;	/* value result */
179 	register struct lpadevice *lpaaddr = (struct lpadevice *)reg;
180 
181 #ifdef lint
182 	br = 0; cvec = br; br = cvec;
183 #endif
184 	/* this should force an interrupt, stall, clear the lpa */
185 	br = 0x15;
186 	cvec = 0330;
187 TRACER("PROBE\n");
188 	return (sizeof (struct lpadevice));
189 }
190 
191 lpaattach(ui)
192 	register struct upa_device *ui;
193 {
194 
195 }
196 
197 lpaopen(dev, flag)
198 	dev_t dev;
199 	int flag;
200 {
201 	register int unit = LPAUNIT(dev);
202 	register struct lpa_softc *sc = &lpa_softc[unit];
203 	register struct uba_device *ui = lpadinfo[unit];
204 	register struct lpadevice *lpaaddr = (struct lpadevice *) ui->ui_addr;
205 
206 TRACER("OPEN\n");
207 	if (unit >= NLPA || sc->sc_flag & OPEN || ui == 0 ||
208 	    ui->ui_alive == 0)
209 		return (ENXIO);
210 	(void) spl7();
211 	lpaaddr->lcim = RESET;
212 	lpaaddr->lcim = 0;
213 	(void) spl0();
214 	lpaaddr->lcos = 0;	/* clear the registers as a precaution */
215 	lpaaddr->lrda = 0;
216 	lpaaddr->lms = 0;
217 	sc->sc_flag = OPEN;
218 	sc->sc_device = LPADEVICE(dev);
219 	sc->sc_channel = LPACHANNEL(dev);
220 	sc->sc_buffer = geteblk();
221 	sc->sc_buffer->b_error = 0;
222 	sc->sc_buffer->b_proc = u.u_procp;
223 	sc->sc_ubufn = -1;
224 	/* THIS SHOULD EVENTUALLY SPECIFY "REAL-TIME" */
225 	u.u_procp->p_nice = NICE;
226 	return (0);
227 }
228 
229 lpaclose(dev, flag)
230 	dev_t dev;
231 	int flag;
232 {
233 	register int unit = LPAUNIT(dev);
234 	register struct lpa_softc *sc = &lpa_softc[unit];
235 	register struct uba_device *ui = lpadinfo[unit];
236 	register struct lpadevice *lpaaddr = (struct lpadevice *) ui->ui_addr;
237 
238 	if (sc->sc_device && sc->sc_ubufn >= 0 && (sc->sc_flag & ERROR) == 0) {
239 		if (sc->sc_start)
240 			lpacmd(sc->sc_buffer, lpaaddr, sc, ui->ui_ubanum);
241 		sc->sc_flag |= STOP;
242 		(void) spl5();
243 		while (sc->sc_flag & STOP) {
244 TRACER("SLEEP\n");
245 			sc->sc_flag |= SLEEP;
246 			sleep((caddr_t)sc, LPAPRI);
247 		}
248 	}
249 	(void) spl7();
250 	lpaaddr->lcim = RESET;
251 	lpaaddr->lcim = 0;
252 	(void) spl0();
253 	if (sc->sc_ubabuf) {
254 		ubarelse(ui->ui_ubanum, &sc->sc_ubabuf);
255 		sc->sc_ubabuf = 0;
256 		(void) spl6();
257 		vsunlock(sc->sc_ubuffer.b_un.b_addr, sc->sc_ubuffer.b_bcount,
258 			(sc->sc_device)? B_READ : B_WRITE);
259 		u.u_procp->p_flag &= ~SPHYSIO;
260 		(void) spl0();
261 	}
262 	if (sc->sc_ubaustat) {
263 		ubarelse(ui->ui_ubanum, &sc->sc_ubaustat);
264 		sc->sc_ubaustat = 0;
265 	}
266 	if (sc->sc_buffer) {
267 		brelse(sc->sc_buffer);
268 		sc->sc_buffer = 0;
269 	}
270 	sc->sc_flag = 0;
271 TRACER("CLOSE\n");
272 }
273 
274 lpawrite(dev, uio)
275 	dev_t dev;
276 	struct uio *uio;
277 {
278 	register int unit = LPAUNIT(dev);
279 	register struct lpa_softc *sc = &lpa_softc[unit];
280 	register struct uba_device *ui = lpadinfo[unit];
281 	register struct lpadevice *lpaaddr = (struct lpadevice *) ui->ui_addr;
282 	register int f;
283 
284 TRACER("WRITE\n");
285 	f = sc->sc_flag;
286 	if ((f & OPEN) == 0)
287 		return (ENXIO);
288 	if ((f & MCODE) == 0)		/* first write is the microcode */
289 		return (lpamcode(lpaaddr, sc, uio));
290 	if ((f & DMDT) == 0)		/* second write is the dispatch table */
291 		return (lpadmdt(lpaaddr, sc, ui->ui_ubanum, uio));
292 	return (ENXIO);
293 }
294 
295 lpamcode(lpaaddr, sc, uio)
296 	register struct lpadevice *lpaaddr;
297 	register struct lpa_softc *sc;
298 	struct uio *uio;
299 {
300 	short v, r;
301 	register int mcaddr;
302 	int error;
303 
304 	mcaddr = 0;
305 	while (uio->uio_resid) {
306 		error = uiomove(&v, 2, UIO_WRITE, uio);
307 		if (error)
308 			break;
309 		lpaaddr->lcim = 0;		/* load microcode word */
310 		lpaaddr->lrda = mcaddr;
311 		lpaaddr->lms = v;
312 		lpaaddr->lcim = ROMO;
313 		lpaaddr->lcim |= CWRITE;
314 		lpaaddr->lcim = 0;		/* verify microcode word */
315 		lpaaddr->lrda = mcaddr;
316 		lpaaddr->lcim = ROMO;
317 		if ((r = lpaaddr->lms) != v) {
318 			/* download failure */
319 			printf("LPA MICROCODE FAIL: exp:%o got:%o\n", v, r);
320 			return (ENXIO);
321 		}
322 		mcaddr++;
323 	}
324 	lpaaddr->lcim = RUN | EA;	/* turn it on */
325 	sc->sc_flag |= MCODE;
326 	lpaaddr->lcim |= IIE;
327 	lpaaddr->lcos |= OIE;
328 	return (error);
329 TRACER("MCODE\n");
330 }
331 
332 lpadmdt(lpaaddr, sc, ubanum, uio)
333 	register struct lpadevice *lpaaddr;
334 	register struct lpa_softc *sc;
335 	register short ubanum;
336 	struct uio *uio;
337 {
338 	register short *p;
339 	register int n;
340 	int error;
341 
342 	p = (short *) sc->sc_buffer->b_un.b_addr;		/* INIT */
343 	*p++ = (MCVERS << 8) | INIT;	/* mode */
344 	*p++ = ACLOCKA;		/* LPA bus device addresses */
345 	*p++ = ACLOCKB;
346 	*p++ = AAD1;
347 	*p++ = AAD2;
348 	*p++ = ADA;
349 	*p++ = ADIO1;
350 	*p++ = ADIO2;
351 	*p++ = ADIO3;
352 	*p++ = ADIO4;
353 	*p++ = ADIO5;
354 	n = min(uio->uio_resid, 256);	/* dedicated mode dispatch table */
355 	error = uiomove((char *)p, n, UIO_WRITE, uio);
356 	if (error)
357 		return (error);
358 	n >>= 1;
359 	p += n;
360 	while (n++ < 128)
361 		*p++ = 0;
362 	lpacmd(sc->sc_buffer, lpaaddr, sc, ubanum);
363 	sc->sc_flag |= DMDT;
364 	return (0);
365 TRACER("DMDT\n");
366 }
367 
368 lpaioctl(dev, cmd, data, flag)
369 	dev_t dev;
370 	caddr_t data;
371 {
372 	register int unit = LPAUNIT(dev);
373 	register struct lpa_softc *sc = &lpa_softc[unit];
374 	register struct uba_device *ui = lpadinfo[unit];
375 	register struct lpadevice *lpaaddr = (struct lpadevice *) ui->ui_addr;
376 	register short *p;
377 	register int i;
378 	register int v;
379 	struct iocb {
380 		short *baddr;
381 		short rate;
382 		short wc;
383 	} *iocb;
384 
385 TRACER("IOCTL IN\n");
386 	if (cmd != TIOCSETP || (sc->sc_flag & DMDT) == 0)
387 		return (ENXIO);
388 	iocb = (struct iocb *)data;
389 	p = (short *) sc->sc_buffer->b_un.b_addr;	/* CLOCK START */
390 	*p++ = CLOCK | CLOCKA;			/* mode */
391 	*p++ = ENACTR | R1M | MFIE | MRI;	/* clock status */
392 	*p = iocb->rate;			/* clock preset */
393 	lpacmd(sc->sc_buffer, lpaaddr, sc, ui->ui_ubanum);
394 TRACER("CLOCK STARTED\n");
395 	p = (short *) sc->sc_buffer->b_un.b_addr;	/* DATA TRANSFER START*/
396 	*p++ = (sc->sc_device << 7) | DTS | SCHAN;	/* mode */
397 	sc->sc_count = iocb->wc & 017777;	/* word count per buffer */
398 	*p++ = sc->sc_count;
399 							/* user status word */
400 	sc->sc_ustatbuf.b_un.b_addr = (caddr_t) &sc->sc_ustat;
401 	sc->sc_ustatbuf.b_flags = 0;
402 	sc->sc_ustatbuf.b_bcount = 2;
403 	sc->sc_ustatbuf.b_proc = u.u_procp;
404 	sc->sc_ubaustat = ubasetup(ui->ui_ubanum, &sc->sc_ustatbuf, 0);
405 	v = sc->sc_ubaustat;
406 	*p++ = v;
407 	*p = (v >> 16) & 03;		/* into low portion of word */
408 	sc->sc_nbuf = (iocb->wc >> 13) & 07;	/* number of buffers */
409 	*p++ |= sc->sc_nbuf++ << 8;		/* into high portion of word */
410 					/* buffer addresses */
411 	if (useracc(sc->sc_ubuffer.b_un.b_addr = (caddr_t) iocb->baddr,
412 	    sc->sc_ubuffer.b_bcount = sc->sc_count * sc->sc_nbuf * 2,
413 	    (i = (sc->sc_device)? B_READ : B_WRITE) ) == NULL) {
414 TRACER("USER BUFFER FAULT\n");
415 		return (EFAULT);
416 	}
417 	sc->sc_ubuffer.b_flags = B_PHYS | B_BUSY | i;
418 	sc->sc_ubuffer.b_proc = u.u_procp;
419 	u.u_procp->p_flag |= SPHYSIO;
420 	vslock(sc->sc_ubuffer.b_un.b_addr, sc->sc_ubuffer.b_bcount);
421 	sc->sc_ubabuf = ubasetup(ui->ui_ubanum, &sc->sc_ubuffer, 0);
422 	v = sc->sc_ubabuf;
423 	for (i = 0; i < sc->sc_nbuf; i++) {
424 		*p++ = v;
425 		*p++ = (v >> 16) & 03;
426 		v += sc->sc_count * 2;
427 	}
428 	for ( ; i <= 7; i++) {
429 		*p++ = 0;
430 		*p++ = 0;
431 	}
432 	*p++ = 0; *p++ = 0;		/* random channel list address */
433 	*p++ = 0;			/* delay */
434 	*p++ = sc->sc_channel;		/* start channel, channel inc */
435 	*p++ = 1;			/* number of samples in a sequence */
436 	*p++ = 0;			/* dwell */
437 	*p++ = 0;			/* start word no., event mark word */
438 	*p++ = 0;			/* start word mask */
439 	*p = 0;				/* event mark mask */
440 	sc->sc_ustat = 0;
441 	sc->sc_start = (sc->sc_device)? sc->sc_nbuf+1 : 1;
442 	sc->sc_lbufn = 0;
443 	sc->sc_lbufnx = 0;
444 	sc->sc_flag |= STTY;
445 TRACER("IOCTL OUT\n");
446 	return (0);
447 }
448 
449 lparead(dev, uio)
450 	dev_t dev;
451 	struct uio *uio;
452 {
453 	register int unit = LPAUNIT(dev);
454 	register struct lpa_softc *sc = &lpa_softc[unit];
455 	register struct uba_device *ui = lpadinfo[unit];
456 	register struct lpadevice *lpaaddr = (struct lpadevice *) ui->ui_addr;
457 
458 TRACER("READ\n");
459 	if ((sc->sc_flag & STTY) == 0)
460 		return (ENXIO);
461 	if (sc->sc_flag & ERROR)
462 		return (ENXIO);
463 	if (sc->sc_start)
464 		if (--sc->sc_start == 0) {
465 			lpacmd(sc->sc_buffer, lpaaddr, sc, ui->ui_ubanum);
466 TRACER("START\n");
467 		}
468 	inc(sc_ubufn);
469 	if (sc->sc_start == 0) {
470 		(void) spl5();
471 		while (sc->sc_ubufn == sc->sc_lbufn) {
472 			if (sc->sc_flag & ERROR)
473 				return (ENXIO);
474 TRACER("SLEEP\n");
475 			sc->sc_flag |= SLEEP;
476 			sleep(sc, LPAPRI);
477 		}
478 		(void) spl0();
479 	}
480 TRACERN("READ %d\n", sc->sc_ubufn);
481 	return (uiomove(&sc->sc_ubufn, 1, UIO_READ, uio));
482 }
483 
484 lpacmd(bp, lpaaddr, sc, ubanum)
485 	register struct buf *bp;
486 	register struct lpadevice *lpaaddr;
487 	register struct lpa_softc *sc;
488 	register short ubanum;
489 {
490 	int ubareg;
491 
492 TRACER("CMD\n");
493 	ubareg = ubasetup(ubanum, bp, UBA_NEEDBDP);
494 	lpawait(lpaaddr, sc);
495 	lpaaddr->lrda = ubareg;
496 	lpaaddr->lcim &= ~RDAEXT;
497 	lpaaddr->lcim |= ((ubareg >> (16-RDAEXTOFFSET)) & RDAEXT) | GO;
498 	lpawait(lpaaddr, sc);
499 	ubarelse(ubanum, &ubareg);
500 }
501 
502 lpawait(lpaaddr, sc)
503 	register struct lpadevice *lpaaddr;
504 	register struct lpa_softc *sc;
505 {
506 
507 	(void) spl5();
508 	while ((lpaaddr->lcim & READYI) == 0) {
509 TRACER("SLEEP\n");
510 		sc->sc_flag |= SLEEP;
511 		sleep((caddr_t)sc, LPAPRI);
512 	}
513 	(void) spl0();
514 }
515 
516 lpaiintr(unit)
517 	int unit;
518 {
519 	register struct lpa_softc *sc = &lpa_softc[unit];
520 
521 TRACER("{I");
522 	if (sc->sc_flag & SLEEP) {
523 TRACER("<WAKEUP>");
524 		wakeup((caddr_t)sc);
525 		sc->sc_flag &= ~SLEEP;
526 	}
527 TRACER("}");
528 }
529 
530 lpaointr(unit)
531 	int unit;
532 {
533 	register int c, m;
534 	register struct lpa_softc *sc = &lpa_softc[unit];
535 	register struct uba_device *ui = lpadinfo[unit];
536 	register struct lpadevice *lpaaddr = (struct lpadevice *) ui->ui_addr;
537 	int spx;
538 
539 TRACER("{O");
540 	if (sc->sc_flag & SLEEP) {
541 TRACER("<WAKEUP>");
542 		wakeup(sc);
543 		sc->sc_flag &= ~SLEEP;
544 	}
545 	c = lpaaddr->lcos;
546 	m = lpaaddr->lms;
547 	lpaaddr->lcos &= ~READYO;
548 	if (c & ERROR) {
549 TRACER("<ERROR>");
550 		c = (c >> 8) & 0377;
551 		if ((sc->sc_flag & STOP) == 0 || (c != OVERRUN)) {
552 			printf("LPA ERROR %o %o\n", c, m&0177777);
553 			sc->sc_flag |= ERROR;
554 		}
555 		sc->sc_flag &= ~STOP;
556 TRACER("}\n");
557 		return;
558 	}
559 TRACERN("<LPA %d>", sc->sc_lbufnx);
560 	sc->sc_lbufn = sc->sc_lbufnx;
561 	if (sc->sc_ubufn == sc->sc_lbufnx && c & ECODE) {
562 TRACER("<STOP?>");
563 		if (sc->sc_flag & STOP)
564 			return;
565 		printf("LPA OVERRUN\n");
566 		sc->sc_flag |= ERROR;
567 	}
568 	inc(sc_lbufnx);
569 TRACERN("<USTAT %o>", sc->sc_ustat);
570 	spx = spl7();
571 	sc->sc_ustat &= ~NBI;
572 	sc->sc_ustat |= sc->sc_lbufnx << 8;
573 	sc->sc_ustat &= ~DONE;
574 	(void) splx(spx);
575 TRACERN("<LPAN %d>}", sc->sc_lbufnx);
576 }
577 
578 lpareset(uban)
579 	int uban;
580 {
581 	register struct uba_device *ui;
582 	register struct lpadevice *lpaaddr;
583 	register struct lpa_softc *sc;
584 	register int unit;
585 
586 TRACER("LPA RESET\n");
587 	for (unit = 0; unit < NLPA; unit++) {
588 		if ((ui = lpadinfo[unit]) == 0 ||
589 		    ui->ui_ubanum != uban || ui->ui_alive == 0)
590 			continue;
591 		printf(" lpa%d", unit);
592 		lpaaddr = (struct lpadevice *)ui->ui_addr;
593 		sc = &lpa_softc[unit];
594 		sc->sc_flag |= ERROR;
595 		(void) spl7();
596 		lpaaddr->lcim = RESET;
597 		lpaaddr->lcim = 0;
598 		(void) spl0();
599 		if (sc->sc_flag & SLEEP) {
600 			wakeup((caddr_t)sc);
601 			sc->sc_flag &= ~SLEEP;
602 		}
603 	}
604 }
605 #endif NLPA
606