vsbus.c revision 1.6 1 /* $NetBSD: vsbus.c,v 1.6 1997/03/22 23:05:31 ragge Exp $ */
2 /*
3 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Ludd by Bertram Barth.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed at Ludd, University of
19 * Lule}, Sweden and its contributors.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/buf.h>
38 #include <sys/conf.h>
39 #include <sys/file.h>
40 #include <sys/ioctl.h>
41 #include <sys/proc.h>
42 #include <sys/user.h>
43 #include <sys/map.h>
44 #include <sys/device.h>
45 #include <sys/dkstat.h>
46 #include <sys/disklabel.h>
47 #include <sys/syslog.h>
48 #include <sys/stat.h>
49
50 #include <machine/pte.h>
51 #include <machine/sid.h>
52 #include <machine/scb.h>
53 #include <machine/cpu.h>
54 #include <machine/trap.h>
55 #include <machine/nexus.h>
56
57 #include <machine/uvax.h>
58 #include <machine/ka410.h>
59 #include <machine/ka43.h>
60
61 #include <machine/vsbus.h>
62
63 #define trace(x)
64 #define debug(x)
65
66 int vsbus_match __P((struct device *, void *, void *));
67 void vsbus_attach __P((struct device *, struct device *, void *));
68 int vsbus_print __P((void *, const char *));
69
70 void ka410_attach __P((struct device *, struct device *, void *));
71 void ka43_attach __P((struct device *, struct device *, void *));
72
73 struct cfdriver vsbus_cd = {
74 NULL, "vsbus", DV_DULL
75 };
76 struct cfattach vsbus_ca = {
77 sizeof(struct device), vsbus_match, vsbus_attach
78 };
79
80 /*
81 void vsbus_intr_register __P((struct confargs *ca, int (*)(void*), void*));
82 void vsbus_intr_unregister __P((struct confargs *));
83 */
84
85 void vsbus_intr_dispatch __P((int i));
86
87 #define VSBUS_MAXDEVS 8
88 #define VSBUS_MAXINTR 8
89
90 struct confargs *vsbus_devs = NULL;
91
92 #ifdef VAX410
93 struct confargs ka410_devs[] = {
94 /* name intslot intpri intvec intbit ioaddr */
95 { "dc", 7, 7, 0x2C0, (1<<7), KA410_SER_BASE,
96 6, 6, 0x2C4, (1<<6), 0x01, },
97 { "dc (xmit)", 6, 6, 0x2C4, (1<<6), KA410_SER_BASE, },
98 { "le", 5, 5, 0x250, (1<<5), KA410_LAN_BASE,
99 KA410_NWA_BASE, 0x00, },
100 { "ncr", 1, 1, 0x3F8, (1<<1), KA410_SCS_BASE,
101 KA410_SCS_DADR, KA410_SCS_DCNT, KA410_SCS_DDIR,
102 KA410_DMA_BASE, KA410_DMA_SIZE, 0x00, 0x07, },
103 { "hdc", 0, 0, 0x3FC, (1<<0), KA410_DKC_BASE,
104 0, 0, 0,
105 KA410_DMA_BASE, KA410_DMA_SIZE, 0x00, },
106 #if 0
107 { "dc (recv)", 7, 7, 0x2C0, (1<<7), KA410_SER_BASE, },
108 { "dc (xmit)", 6, 6, 0x2C4, (1<<6), KA410_SER_BASE, },
109 { "hdc9224", 0, 0, 0x3FC, (1<<0), KA410_DKC_BASE, },
110 { "ncr5380", 1, 1, 0x3F8, (1<<1), KA410_SCS_BASE, },
111 { "am7990", 5, 5, 0x250, (1<<5), KA410_LAN_BASE, },
112 { "NETOPT", 4, 4, 0x254, (1<<4), KA410_LAN_BASE, },
113 #endif
114 { "" },
115 };
116
117 /*
118 * It would be better if we could use the (provided) system config
119 * information for each CPU instead of this.
120 */
121 struct confargs ka420_devs[] = {
122 { "le", 5, 5, 0x250, (1<<5), KA410_LAN_BASE,
123 KA410_NWA_BASE, 0x00, },
124 { "ncr", 1, 1, 0x3F8, (1<<1), KA410_SCS_BASE,
125 KA410_SCS_DADR, KA410_SCS_DCNT, KA410_SCS_DDIR,
126 KA410_DMA_BASE, KA410_DMA_SIZE, 0x00, 0x07, },
127 { "ncr", 0, 0, 0x3FC, (1<<0), 0x200C0180,
128 0x200C01A0, 0x200C01C0, 0x200C01C4,
129 KA410_DMA_BASE, KA410_DMA_SIZE, 0x00, 0x07, },
130 { "" },
131 };
132 #endif
133
134 #ifdef VAX43
135 struct confargs ka43_devs[] = {
136 /* name intslot intpri intvec intbit ioaddr */
137 { "dc", 7, 7, 0x2C0, (1<<7), KA43_SER_BASE,
138 6, 6, 0x2C4, (1<<6), 0x01, },
139 { "dc (xmit)", 6, 6, 0x2C4, (1<<6), KA43_SER_BASE, },
140 { "le", 5, 5, 0x250, (1<<5), KA43_LAN_BASE,
141 KA43_NWA_BASE, 0x00, },
142 { "ncr", 1, 1, 0x3F8, (1<<1), KA43_SC1_BASE,
143 KA43_SC1_DADR, KA43_SC1_DCNT, KA43_SC1_DDIR,
144 KA43_DMA_BASE, KA43_DMA_SIZE, 0x01, 0x06, },
145 { "ncr", 0, 0, 0x3FC, (1<<0), KA43_SC2_BASE,
146 KA43_SC2_DADR, KA43_SC2_DCNT, KA43_SC2_DDIR,
147 KA43_DMA_BASE, KA43_DMA_SIZE, 0x01, 0x06, },
148 #if 0
149 { "le (2nd)", 4, 4, 0x254, (1<<4), 0x???, },
150 { "NETOPT", 4, 4, 0x254, (1<<4), 0x???, },
151 #endif
152 { "" },
153 };
154 #endif
155
156 int
157 vsbus_print(aux, name)
158 void *aux;
159 const char *name;
160 {
161 struct confargs *ca = aux;
162
163 trace(("vsbus_print(%x, %s)\n", ca->ca_name, name));
164
165 if (name) {
166 printf ("device %s at %s", ca->ca_name, name);
167 return (UNSUPP);
168 }
169 return (UNCONF);
170 }
171
172 int
173 vsbus_match(parent, cf, aux)
174 struct device *parent;
175 void *cf;
176 void *aux;
177 {
178 struct bp_conf *bp = aux;
179
180 trace(("vsbus_match: bp->type = \"%s\"\n", bp->type));
181
182 if (strcmp(bp->type, "vsbus"))
183 return 0;
184 /*
185 * on machines which can have it, the vsbus is always there
186 */
187 if ((vax_bustype & VAX_VSBUS) == 0)
188 return (0);
189
190 return (1);
191 }
192
193 #if 1 /*------------------------------------------------------------*/
194 #if 1
195 #define REG(name) short name; short X##name##X;
196 #else
197 #define REG(name) int name;
198 #endif
199 static volatile struct {/* base address of DZ-controller: 0x200A0000 */
200 REG(csr); /* 00 Csr: control/status register */
201 REG(rbuf); /* 04 Rbuf/Lpr: receive buffer/line param reg. */
202 REG(tcr); /* 08 Tcr: transmit console register */
203 REG(tdr); /* 0C Msr/Tdr: modem status reg/transmit data reg */
204 REG(lpr0); /* 10 Lpr0: */
205 REG(lpr1); /* 14 Lpr0: */
206 REG(lpr2); /* 18 Lpr0: */
207 REG(lpr3); /* 1C Lpr0: */
208 } *dz = (void*)0x200A0000;
209 extern int dzcnrint();
210 extern int dzcntint();
211 int hardclock_count = 0;
212 int
213 ka410_consintr_enable()
214 {
215 vsbus_intr_enable(&ka410_devs[0]);
216 vsbus_intr_enable(&ka410_devs[1]);
217 }
218
219 int
220 ka410_consRecv_intr(p)
221 void *p;
222 {
223 /* printf("ka410_consRecv_intr: hc-count=%d\n", hardclock_count); */
224 dzcnrint();
225 /* printf("gencnrint() returned.\n"); */
226 return(0);
227 }
228
229 int
230 ka410_consXmit_intr(p)
231 void *p;
232 {
233 /* printf("ka410_consXmit_intr: hc-count=%d\n", hardclock_count); */
234 dzcntint();
235 /* printf("gencntint() returned.\n"); */
236 return(0);
237 }
238 #endif /*------------------------------------------------------------*/
239
240 void
241 vsbus_attach(parent, self, aux)
242 struct device *parent, *self;
243 void *aux;
244 {
245 struct confargs *ca;
246 int i;
247
248 printf("\n");
249 trace (("vsbus_attach()\n"));
250
251 switch (vax_boardtype) {
252 case VAX_BTYP_420:
253 vsbus_devs = ka420_devs;
254 break;
255
256 case VAX_BTYP_410:
257 vsbus_devs = ka410_devs;
258 break;
259
260 case VAX_BTYP_43:
261 case VAX_BTYP_46:
262 case VAX_BTYP_49:
263 #ifdef VAX43
264 vsbus_devs = ka43_devs;
265 #endif
266 break;
267
268 default:
269 printf ("unsupported boardtype 0x%x in vsbus_attach()\n",
270 vax_boardtype);
271 return;
272 }
273
274 /*
275 * first setup interrupt-table, so that devices can register
276 * their interrupt-routines...
277 */
278 vsbus_intr_setup();
279
280 /*
281 * now check for all possible devices on this "bus"
282 */
283 for (i=0; i<VSBUS_MAXDEVS; i++) {
284 ca = &vsbus_devs[i];
285 if (*ca->ca_name == '\0')
286 break;
287 config_found(self, (void*)ca, vsbus_print);
288 }
289
290 /*
291 * as long as there's no working DZ-driver, we use this dummy
292 */
293 vsbus_intr_register(&ka410_devs[0], ka410_consRecv_intr, NULL);
294 vsbus_intr_register(&ka410_devs[1], ka410_consXmit_intr, NULL);
295 }
296
297 #define VSBUS_MAX_INTR 8 /* 64? */
298 /*
299 * interrupt service routines are given an int as argument, which is
300 * pushed onto stack as LITERAL. Thus the value is between 0-63.
301 * This array of 64 might be oversized for now, but it's all which
302 * ever will be possible.
303 */
304 struct vsbus_ivec {
305 struct ivec_dsp intr_vec; /* this is referenced in SCB */
306 int intr_count; /* keep track of interrupts */
307 int intr_flags; /* valid, etc. */
308 void (*enab)(int); /* enable interrupt */
309 void (*disab)(int); /* disable interrupt */
310 void (*prep)(int); /* need pre-processing? */
311 int (*handler)(void*); /* isr-routine to call */
312 void *hndlarg; /* args to this routine */
313 void (*postp)(int); /* need post-processing? */
314 } vsbus_ivtab[VSBUS_MAX_INTR];
315
316 /*
317 *
318 */
319 int
320 vsbus_intr_setup()
321 {
322 int i;
323 struct vsbus_ivec *ip;
324 extern struct ivec_dsp idsptch; /* subr.s */
325
326 for (i=0; i<VSBUS_MAX_INTR; i++) {
327 ip = &vsbus_ivtab[i];
328 bcopy(&idsptch, &ip->intr_vec, sizeof(struct ivec_dsp));
329 ip->intr_vec.pushlarg = i;
330 ip->intr_vec.hoppaddr = vsbus_intr_dispatch;
331 ip->intr_count = 0;
332 ip->intr_flags = 0;
333 ip->enab = NULL;
334 ip->disab = NULL;
335 ip->postp = NULL;
336 }
337 switch (vax_boardtype) {
338 case VAX_BTYP_410:
339 case VAX_BTYP_420:
340 case VAX_BTYP_43:
341 case VAX_BTYP_46:
342 case VAX_BTYP_49:
343 ka410_intr_setup();
344 return(0);
345 default:
346 printf("unsupported board-type 0x%x in vsbus_intr_setup()\n",
347 vax_boardtype);
348 return(1);
349 }
350 }
351
352 int
353 vsbus_intr_register(ca, handler, arg)
354 struct confargs *ca;
355 int (*handler)(void*);
356 void *arg;
357 {
358 /* struct device *dev = arg; */
359 int i = ca->ca_intslot;
360 struct vsbus_ivec *ip = &vsbus_ivtab[i];
361
362 trace (("vsbus_intr_register(%s/%d)\n", ca->ca_name, ca->ca_intslot));
363
364 ip->handler = handler;
365 ip->hndlarg = arg;
366 }
367
368 int
369 vsbus_intr_enable(ca)
370 struct confargs *ca;
371 {
372 int i = ca->ca_intslot;
373 struct vsbus_ivec *ip = &vsbus_ivtab[i];
374
375 trace (("vsbus_intr_enable(%s/%d)\n", ca->ca_name, ca->ca_intslot));
376
377 /* XXX check for valid handler etc. !!! */
378 if (ip->handler == NULL) {
379 printf("interrupts for \"%s\"(%d) not enabled: null-handler\n",
380 ca->ca_name, ca->ca_intslot);
381 return;
382 }
383
384 ip->enab(i);
385 }
386
387 int
388 vsbus_intr_disable(ca)
389 struct confargs *ca;
390 {
391 int i = ca->ca_intslot;
392 struct vsbus_ivec *ip = &vsbus_ivtab[i];
393
394 trace (("vsbus_intr_disable(%s/%d)\n", ca->ca_name, i));
395
396 ip->disab(i);
397 }
398
399 int
400 vsbus_intr_unregister(ca)
401 struct confargs *ca;
402 {
403 int i = ca->ca_intslot;
404 struct vsbus_ivec *ip = &vsbus_ivtab[i];
405
406 trace (("vsbus_intr_unregister(%s/%d)\n", ca->ca_name, i));
407
408 ip->handler = NULL;
409 ip->hndlarg = NULL;
410 }
411
412 void
413 vsbus_intr_dispatch(i)
414 register int i;
415 {
416 register struct vsbus_ivec *ip = &vsbus_ivtab[i];
417
418 trace (("vsbus_intr_dispatch(%d)", i));
419
420 if (i < VSBUS_MAX_INTR && ip->handler != NULL) {
421 ip->intr_count++;
422 debug (("intr-count[%d] = %d\n", i, ip->intr_count));
423 (ip->handler)(ip->hndlarg);
424 if (ip->postp)
425 (ip->postp)(i);
426 return;
427 }
428
429 if (i < 0 || i >= VSBUS_MAX_INTR) {
430 printf ("stray interrupt %d on vsbus.\n", i);
431 return;
432 }
433
434 if (!ip->handler) {
435 printf ("unhandled interrupt %d on vsbus.\n", i);
436 return;
437 }
438 }
439
440 /*
441 * These addresses are invalid and will be updated/corrected by
442 * ka410_intr_setup(), but having them this way helps debugging
443 */
444 static volatile u_char *ka410_intmsk = (void*)KA410_INTMSK;
445 static volatile u_char *ka410_intreq = (void*)KA410_INTREQ;
446 static volatile u_char *ka410_intclr = (void*)KA410_INTCLR;
447
448 static void
449 ka410_intr_enable(i)
450 int i;
451 {
452 trace (("ka410_intr_enable(%d)\n", i));
453 *ka410_intmsk |= (1<<i);
454 }
455
456 static void
457 ka410_intr_disable(i)
458 int i;
459 {
460 trace (("ka410_intr_disable(%d)\n", i));
461 *ka410_intmsk &= ~(1<<i);
462 }
463
464 static void
465 ka410_intr_clear(i)
466 int i;
467 {
468 trace (("ka410_intr_clear(%d)\n", i));
469 *ka410_intclr = (1<<i);
470 }
471
472 ka410_intr_setup()
473 {
474 int i;
475 struct vsbus_ivec *ip;
476 void **scbP = (void*)scb;
477
478 trace (("ka410_intr_setup()\n"));
479
480 ka410_intmsk = (void*)uvax_phys2virt(KA410_INTMSK);
481 ka410_intreq = (void*)uvax_phys2virt(KA410_INTREQ);
482 ka410_intclr = (void*)uvax_phys2virt(KA410_INTCLR);
483
484 *ka410_intmsk = 0; /* disable all interrupts */
485 *ka410_intclr = 0xFF; /* clear all old interrupts */
486
487 /*
488 * insert the VS2000-specific routines into ivec-table...
489 */
490 for (i=0; i<8; i++) {
491 ip = &vsbus_ivtab[i];
492 ip->enab = ka410_intr_enable;
493 ip->disab = ka410_intr_disable;
494 /* ip->postp = ka410_intr_clear; bertram XXX */
495 }
496 /*
497 * ...and register the interrupt-vectors in SCB
498 */
499 scbP[IVEC_DC/4] = &vsbus_ivtab[0].intr_vec;
500 scbP[IVEC_SC/4] = &vsbus_ivtab[1].intr_vec;
501 scbP[IVEC_VS/4] = &vsbus_ivtab[2].intr_vec;
502 scbP[IVEC_VF/4] = &vsbus_ivtab[3].intr_vec;
503 scbP[IVEC_NS/4] = &vsbus_ivtab[4].intr_vec;
504 scbP[IVEC_NP/4] = &vsbus_ivtab[5].intr_vec;
505 scbP[IVEC_ST/4] = &vsbus_ivtab[6].intr_vec;
506 scbP[IVEC_SR/4] = &vsbus_ivtab[7].intr_vec;
507 }
508
509 /*
510 *
511 *
512 */
513
514 static volatile struct dma_lock {
515 int dl_locked;
516 int dl_wanted;
517 void *dl_owner;
518 int dl_count;
519 } dmalock = { 0, 0, NULL, 0 };
520
521 int
522 vsbus_lockDMA(ca)
523 struct confargs *ca;
524 {
525 while (dmalock.dl_locked) {
526 dmalock.dl_wanted++;
527 sleep((caddr_t)&dmalock, PRIBIO); /* PLOCK or PRIBIO ? */
528 dmalock.dl_wanted--;
529 }
530 dmalock.dl_locked++;
531 dmalock.dl_owner = ca;
532
533 /*
534 * no checks yet, no timeouts, nothing...
535 */
536
537 #ifdef DEBUG
538 if ((++dmalock.dl_count % 1000) == 0)
539 printf("%d locks, owner: %s\n", dmalock.dl_count, ca->ca_name);
540 #endif
541 return (0);
542 }
543
544 int
545 vsbus_unlockDMA(ca)
546 struct confargs *ca;
547 {
548 if (dmalock.dl_locked != 1 || dmalock.dl_owner != ca) {
549 printf("locking-problem: %d, %s\n", dmalock.dl_locked,
550 (dmalock.dl_owner ? dmalock.dl_owner : "null"));
551 dmalock.dl_locked = 0;
552 return (-1);
553 }
554 dmalock.dl_owner = NULL;
555 dmalock.dl_locked = 0;
556 if (dmalock.dl_wanted) {
557 wakeup((caddr_t)&dmalock);
558 }
559 return (0);
560 }
561
562 /*----------------------------------------------------------------------*/
563 #if 0
564 /*
565 * small set of routines needed for mapping when doing pseudo-DMA,
566 * quasi-DMA or virtual-DMA (choose whatever name you like).
567 *
568 * Once I know how VS3100 is doing real DMA (I hope it does), this
569 * should be rewritten to present a general interface...
570 *
571 */
572
573 extern u_long uVAX_physmap;
574
575 u_long
576 vsdma_mapin(bp, len)
577 struct buf *bp;
578 int len;
579 {
580 pt_entry_t *pte; /* pointer to Page-Table-Entry */
581 struct pcb *pcb; /* pointer to Process-Controll-Block */
582 pt_entry_t *xpte;
583 caddr_t addr;
584 int pgoff; /* offset into 1st page */
585 int pgcnt; /* number of pages needed */
586 int pfnum;
587 int i;
588
589 trace(("mapin(bp=%x, bp->data=%x)\n", bp, bp->b_data));
590
591 addr = bp->b_data;
592 pgoff = (int)bp->b_data & PGOFSET; /* get starting offset */
593 pgcnt = btoc(bp->b_bcount + pgoff) + 1; /* one more than needed */
594
595 /*
596 * Get a pointer to the pte pointing out the first virtual address.
597 * Use different ways in kernel and user space.
598 */
599 if ((bp->b_flags & B_PHYS) == 0) {
600 pte = kvtopte(addr);
601 } else {
602 pcb = bp->b_proc->p_vmspace->vm_pmap.pm_pcb;
603 pte = uvtopte(addr, pcb);
604 }
605
606 /*
607 * When we are doing DMA to user space, be sure that all pages
608 * we want to transfer to are mapped. WHY DO WE NEED THIS???
609 * SHOULDN'T THEY ALWAYS BE MAPPED WHEN DOING THIS???
610 */
611 for (i=0; i<(pgcnt-1); i++) {
612 if ((pte + i)->pg_pfn == 0) {
613 int rv;
614 rv = vm_fault(&bp->b_proc->p_vmspace->vm_map,
615 (unsigned)addr + i * NBPG,
616 VM_PROT_READ|VM_PROT_WRITE, FALSE);
617 if (rv)
618 panic("vs-DMA to nonexistent page, %d", rv);
619 }
620 }
621
622 /*
623 * now insert new mappings for this memory area into kernel's
624 * mapping-table
625 */
626 xpte = kvtopte(uVAX_physmap);
627 while (--pgcnt > 0) {
628 pfnum = pte->pg_pfn;
629 if (pfnum == 0)
630 panic("vsbus: zero entry");
631 *(int *)xpte++ = *(int *)pte++;
632 }
633 *(int *)xpte = 0; /* mark last mapped page as invalid! */
634
635 debug(("uVAX: 0x%x\n", uVAX_physmap + pgoff));
636
637 return (uVAX_physmap + pgoff); /* ??? */
638 }
639 #endif
640 /*----------------------------------------------------------------------*/
641 /*
642 * Here follows some currently(?) unused stuff. Someday this should be removed
643 */
644
645 #if 0
646 /*
647 * Configure devices on VS2000/KA410 directly attached to vsbus
648 */
649 void
650 ka410_attach(parent, self, aux)
651 struct device *parent;
652 struct device *self;
653 void *aux;
654 {
655 struct confargs *ca;
656 int i;
657
658 for (i=0; i<KA410_MAXDEVS; i++) {
659 ca = &ka410_devs[i];
660 if (*ca->ca_name == '\0')
661 break;
662 config_found(self, (void*)ca, vsbus_print);
663 }
664 /*
665 * as long as there's no real DZ-driver, we used this dummy
666 */
667 vsbus_intr_register(&ka410_devs[0], ka410_consRecv_intr, NULL);
668 vsbus_intr_register(&ka410_devs[1], ka410_consXmit_intr, NULL);
669 }
670
671 #endif
672