if_le_vme.c revision 1.14 1 /* $NetBSD: if_le_vme.c,v 1.14 2002/09/27 15:35:54 provos Exp $ */
2
3 /*-
4 * Copyright (c) 1998 maximum entropy. All rights reserved.
5 * Copyright (c) 1997 Leo Weppelman. All rights reserved.
6 * Copyright (c) 1995 Charles M. Hannum. All rights reserved.
7 * Copyright (c) 1992, 1993
8 * The Regents of the University of California. All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Ralph Campbell and Rick Macklem.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 *
41 * @(#)if_le.c 8.2 (Berkeley) 11/16/93
42 */
43
44 #include "opt_inet.h"
45 #include "bpfilter.h"
46
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/mbuf.h>
50 #include <sys/syslog.h>
51 #include <sys/socket.h>
52 #include <sys/device.h>
53
54 #include <net/if.h>
55 #include <net/if_media.h>
56 #include <net/if_ether.h>
57
58 #ifdef INET
59 #include <netinet/in.h>
60 #include <netinet/if_inarp.h>
61 #endif
62
63 #include <machine/cpu.h>
64 #include <machine/bus.h>
65 #include <machine/iomap.h>
66 #include <machine/scu.h>
67
68 #include <atari/atari/device.h>
69 #include <atari/atari/intr.h>
70
71 #include <dev/ic/lancereg.h>
72 #include <dev/ic/lancevar.h>
73 #include <dev/ic/am7990reg.h>
74 #include <dev/ic/am7990var.h>
75
76 #include <atari/vme/vmevar.h>
77 #include <atari/vme/if_levar.h>
78
79 /*
80 * All cards except BVME410 have 64KB RAM. However.... On the Riebl cards the
81 * area between the offsets 0xee70-0xeec0 is used to store config data.
82 */
83 struct le_addresses {
84 u_long reg_addr;
85 u_long mem_addr;
86 int irq;
87 int reg_size;
88 int mem_size;
89 int type_hint;
90 } lestd[] = {
91 { 0xfe00fff0, 0xfe010000, IRQUNK, 16, 64*1024,
92 LE_OLD_RIEBL|LE_NEW_RIEBL }, /* Riebl */
93 { 0xffcffff0, 0xffcf0000, 5, 16, 64*1024,
94 LE_PAM }, /* PAM */
95 { 0xfecffff0, 0xfecf0000, 5, 16, 64*1024,
96 LE_ROTHRON }, /* Rhotron */
97 { 0xfeff4100, 0xfe000000, 4, 8, VMECF_MEMSIZ_DEFAULT,
98 LE_BVME410 } /* BVME410 */
99 };
100
101 #define NLESTD (sizeof(lestd) / sizeof(lestd[0]))
102
103 /*
104 * Default mac for RIEBL cards without a (working) battery. The first 4 bytes
105 * are the manufacturer id.
106 */
107 static u_char riebl_def_mac[] = {
108 0x00, 0x00, 0x36, 0x04, 0x00, 0x00
109 };
110
111 static int le_intr __P((struct le_softc *, int));
112 static void lepseudointr __P((struct le_softc *, void *));
113 static int le_vme_match __P((struct device *, struct cfdata *, void *));
114 static void le_vme_attach __P((struct device *, struct device *, void *));
115 static int probe_addresses __P((bus_space_tag_t *, bus_space_tag_t *,
116 bus_space_handle_t *, bus_space_handle_t *));
117 static void riebl_skip_reserved_area __P((struct lance_softc *));
118 static int nm93c06_read __P((bus_space_tag_t, bus_space_handle_t, int));
119 static int bvme410_probe __P((bus_space_tag_t, bus_space_handle_t));
120 static int bvme410_mem_size __P((bus_space_tag_t, u_long));
121 static void bvme410_copytobuf __P((struct lance_softc *, void *, int, int));
122 static void bvme410_zerobuf __P((struct lance_softc *, int, int));
123
124 struct cfattach le_vme_ca = {
125 sizeof(struct le_softc), le_vme_match, le_vme_attach
126 };
127
128 #if defined(_KERNEL_OPT)
129 #include "opt_ddb.h"
130 #endif
131
132 #ifdef DDB
133 #define integrate
134 #define hide
135 #else
136 #define integrate static __inline
137 #define hide static
138 #endif
139
140 hide void lewrcsr __P((struct lance_softc *, u_int16_t, u_int16_t));
141 hide u_int16_t lerdcsr __P((struct lance_softc *, u_int16_t));
142
143 hide void
144 lewrcsr(sc, port, val)
145 struct lance_softc *sc;
146 u_int16_t port, val;
147 {
148 struct le_softc *lesc = (struct le_softc *)sc;
149 int s;
150
151 s = splhigh();
152 bus_space_write_2(lesc->sc_iot, lesc->sc_ioh, LER_RAP, port);
153 bus_space_write_2(lesc->sc_iot, lesc->sc_ioh, LER_RDP, val);
154 splx(s);
155 }
156
157 hide u_int16_t
158 lerdcsr(sc, port)
159 struct lance_softc *sc;
160 u_int16_t port;
161 {
162 struct le_softc *lesc = (struct le_softc *)sc;
163 u_int16_t val;
164 int s;
165
166 s = splhigh();
167 bus_space_write_2(lesc->sc_iot, lesc->sc_ioh, LER_RAP, port);
168 val = bus_space_read_2(lesc->sc_iot, lesc->sc_ioh, LER_RDP);
169 splx(s);
170
171 return (val);
172 }
173
174 static int
175 le_vme_match(parent, cfp, aux)
176 struct device *parent;
177 struct cfdata *cfp;
178 void *aux;
179 {
180 struct vme_attach_args *va = aux;
181 int i;
182 bus_space_tag_t iot;
183 bus_space_tag_t memt;
184 bus_space_handle_t ioh;
185 bus_space_handle_t memh;
186
187 iot = va->va_iot;
188 memt = va->va_memt;
189
190 for (i = 0; i < NLESTD; i++) {
191 struct le_addresses *le_ap = &lestd[i];
192 int found = 0;
193
194 if ((va->va_iobase != IOBASEUNK)
195 && (va->va_iobase != le_ap->reg_addr))
196 continue;
197
198 if ((va->va_maddr != MADDRUNK)
199 && (va->va_maddr != le_ap->mem_addr))
200 continue;
201
202 if ((le_ap->irq != IRQUNK) && (va->va_irq != le_ap->irq))
203 continue;
204
205 if (bus_space_map(iot, le_ap->reg_addr, le_ap->reg_size, 0, &ioh)) {
206 printf("leprobe: cannot map io-area\n");
207 return (0);
208 }
209 if (le_ap->mem_size == VMECF_MEMSIZ_DEFAULT) {
210 if (bvme410_probe(iot, ioh)) {
211 bus_space_write_2(iot, ioh, BVME410_BAR, 0x1); /* XXX */
212 le_ap->mem_size = bvme410_mem_size(memt, le_ap->mem_addr);
213 }
214 }
215 if (le_ap->mem_size == VMECF_MEMSIZ_DEFAULT) {
216 bus_space_unmap(iot, ioh, le_ap->reg_size);
217 continue;
218 }
219
220 if (bus_space_map(memt, le_ap->mem_addr, le_ap->mem_size, 0, &memh)) {
221 bus_space_unmap(iot, ioh, le_ap->reg_size);
222 printf("leprobe: cannot map memory-area\n");
223 return (0);
224 }
225 found = probe_addresses(&iot, &memt, &ioh, &memh);
226 bus_space_unmap(iot, ioh, le_ap->reg_size);
227 bus_space_unmap(memt, memh, le_ap->mem_size);
228
229 if (found) {
230 va->va_iobase = le_ap->reg_addr;
231 va->va_iosize = le_ap->reg_size;
232 va->va_maddr = le_ap->mem_addr;
233 va->va_msize = le_ap->mem_size;
234 va->va_aux = le_ap;
235 if (va->va_irq == IRQUNK)
236 va->va_irq = le_ap->irq;
237 return 1;
238 }
239 }
240 return (0);
241 }
242
243 static int
244 probe_addresses(iot, memt, ioh, memh)
245 bus_space_tag_t *iot;
246 bus_space_tag_t *memt;
247 bus_space_handle_t *ioh;
248 bus_space_handle_t *memh;
249 {
250 /*
251 * Test accesibility of register and memory area
252 */
253 if(!bus_space_peek_2(*iot, *ioh, LER_RDP))
254 return 0;
255 if(!bus_space_peek_1(*memt, *memh, 0))
256 return 0;
257
258 /*
259 * Test for writable memory
260 */
261 bus_space_write_2(*memt, *memh, 0, 0xa5a5);
262 if (bus_space_read_2(*memt, *memh, 0) != 0xa5a5)
263 return 0;
264
265 /*
266 * Test writability of selector port.
267 */
268 bus_space_write_2(*iot, *ioh, LER_RAP, LE_CSR1);
269 if (bus_space_read_2(*iot, *ioh, LER_RAP) != LE_CSR1)
270 return 0;
271
272 /*
273 * Do a small register test
274 */
275 bus_space_write_2(*iot, *ioh, LER_RAP, LE_CSR0);
276 bus_space_write_2(*iot, *ioh, LER_RDP, LE_C0_INIT | LE_C0_STOP);
277 if (bus_space_read_2(*iot, *ioh, LER_RDP) != LE_C0_STOP)
278 return 0;
279
280 bus_space_write_2(*iot, *ioh, LER_RDP, LE_C0_STOP);
281 if (bus_space_read_2(*iot, *ioh, LER_RDP) != LE_C0_STOP)
282 return 0;
283
284 return 1;
285 }
286
287 /*
288 * Interrupt mess. Because the card's interrupt is hardwired to either
289 * ipl5 or ipl3 (mostly on ipl5) and raising splnet to spl5() just won't do
290 * (it kills the serial at the least), we use a 2-level interrupt scheme. The
291 * card interrupt is routed to 'le_intr'. If the previous ipl was below
292 * splnet, just call the mi-function. If not, save the interrupt status,
293 * turn off card interrupts (the card is *very* persistent) and arrange
294 * for a softint 'callback' through 'lepseudointr'.
295 */
296 static int
297 le_intr(lesc, sr)
298 struct le_softc *lesc;
299 int sr;
300 {
301 struct lance_softc *sc = &lesc->sc_am7990.lsc;
302 u_int16_t csr0;
303
304 if ((sr & PSL_IPL) < (IPL_NET & PSL_IPL))
305 am7990_intr(sc);
306 else {
307 sc->sc_saved_csr0 = csr0 = lerdcsr(sc, LE_CSR0);
308 lewrcsr(sc, LE_CSR0, csr0 & ~LE_C0_INEA);
309 add_sicallback((si_farg)lepseudointr, lesc, sc);
310 }
311 return 1;
312 }
313
314
315 static void
316 lepseudointr(lesc, sc)
317 struct le_softc *lesc;
318 void *sc;
319 {
320 int s;
321
322 s = splx(lesc->sc_splval);
323 am7990_intr(sc);
324 splx(s);
325 }
326
327 static void
328 le_vme_attach(parent, self, aux)
329 struct device *parent, *self;
330 void *aux;
331 {
332 struct le_softc *lesc = (struct le_softc *)self;
333 struct lance_softc *sc = &lesc->sc_am7990.lsc;
334 struct vme_attach_args *va = aux;
335 bus_space_handle_t ioh;
336 bus_space_handle_t memh;
337 struct le_addresses *le_ap;
338 int i;
339
340 printf("\n%s: ", sc->sc_dev.dv_xname);
341
342 if (bus_space_map(va->va_iot, va->va_iobase, va->va_iosize, 0, &ioh))
343 panic("leattach: cannot map io-area");
344 if (bus_space_map(va->va_memt, va->va_maddr, va->va_msize, 0, &memh))
345 panic("leattach: cannot map mem-area");
346
347 lesc->sc_iot = va->va_iot;
348 lesc->sc_ioh = ioh;
349 lesc->sc_memt = va->va_memt;
350 lesc->sc_memh = memh;
351 lesc->sc_splval = (va->va_irq << 8) | PSL_S; /* XXX */
352 le_ap = (struct le_addresses *)va->va_aux;
353
354 /*
355 * Go on to find board type
356 */
357 if ((le_ap->type_hint & LE_PAM)
358 && bus_space_peek_1(va->va_iot, ioh, LER_EEPROM)) {
359 printf("PAM card");
360 lesc->sc_type = LE_PAM;
361 bus_space_read_1(va->va_iot, ioh, LER_MEME);
362 }
363 else if((le_ap->type_hint & LE_BVME410)
364 && bvme410_probe(va->va_iot, ioh)) {
365 printf("BVME410");
366 lesc->sc_type = LE_BVME410;
367 }
368 else if (le_ap->type_hint & (LE_NEW_RIEBL|LE_OLD_RIEBL)) {
369 printf("Riebl card");
370 if(bus_space_read_4(va->va_memt, memh, RIEBL_MAGIC_ADDR)
371 == RIEBL_MAGIC)
372 lesc->sc_type = LE_NEW_RIEBL;
373 else {
374 printf("(without battery) ");
375 lesc->sc_type = LE_OLD_RIEBL;
376 }
377 }
378 else printf("le_vme_attach: Unsupported card!");
379
380 switch (lesc->sc_type) {
381 case LE_BVME410:
382 sc->sc_copytodesc = bvme410_copytobuf;
383 sc->sc_copyfromdesc = lance_copyfrombuf_contig;
384 sc->sc_copytobuf = bvme410_copytobuf;
385 sc->sc_copyfrombuf = lance_copyfrombuf_contig;
386 sc->sc_zerobuf = bvme410_zerobuf;
387 break;
388 default:
389 sc->sc_copytodesc = lance_copytobuf_contig;
390 sc->sc_copyfromdesc = lance_copyfrombuf_contig;
391 sc->sc_copytobuf = lance_copytobuf_contig;
392 sc->sc_copyfrombuf = lance_copyfrombuf_contig;
393 sc->sc_zerobuf = lance_zerobuf_contig;
394 break;
395 }
396
397 sc->sc_rdcsr = lerdcsr;
398 sc->sc_wrcsr = lewrcsr;
399 sc->sc_hwinit = NULL;
400 sc->sc_conf3 = LE_C3_BSWP;
401 sc->sc_addr = 0;
402 sc->sc_memsize = va->va_msize;
403 sc->sc_mem = (void *)memh; /* XXX */
404
405 /*
406 * Get MAC address
407 */
408 switch (lesc->sc_type) {
409 case LE_OLD_RIEBL:
410 bcopy(riebl_def_mac, sc->sc_enaddr,
411 sizeof(sc->sc_enaddr));
412 break;
413 case LE_NEW_RIEBL:
414 for (i = 0; i < sizeof(sc->sc_enaddr); i++)
415 sc->sc_enaddr[i] =
416 bus_space_read_1(va->va_memt, memh, i + RIEBL_MAC_ADDR);
417 break;
418 case LE_PAM:
419 i = bus_space_read_1(va->va_iot, ioh, LER_EEPROM);
420 for (i = 0; i < sizeof(sc->sc_enaddr); i++) {
421 sc->sc_enaddr[i] =
422 (bus_space_read_2(va->va_memt, memh, 2 * i) << 4) |
423 (bus_space_read_2(va->va_memt, memh, 2 * i + 1) & 0xf);
424 }
425 i = bus_space_read_1(va->va_iot, ioh, LER_MEME);
426 break;
427 case LE_BVME410:
428 for (i = 0; i < (sizeof(sc->sc_enaddr) >> 1); i++) {
429 u_int16_t tmp;
430
431 tmp = nm93c06_read(va->va_iot, ioh, i);
432 sc->sc_enaddr[2 * i] = (tmp >> 8) & 0xff;
433 sc->sc_enaddr[2 * i + 1] = tmp & 0xff;
434 }
435 bus_space_write_2(va->va_iot, ioh, BVME410_BAR, 0x1); /* XXX */
436 }
437
438 am7990_config(&lesc->sc_am7990);
439
440 if ((lesc->sc_type == LE_OLD_RIEBL) || (lesc->sc_type == LE_NEW_RIEBL))
441 riebl_skip_reserved_area(sc);
442
443 /*
444 * XXX: We always use uservector 64....
445 */
446 if ((lesc->sc_intr = intr_establish(64, USER_VEC, 0,
447 (hw_ifun_t)le_intr, lesc)) == NULL) {
448 printf("le_vme_attach: Can't establish interrupt\n");
449 return;
450 }
451
452 /*
453 * Notify the card of the vector
454 */
455 switch (lesc->sc_type) {
456 case LE_OLD_RIEBL:
457 case LE_NEW_RIEBL:
458 bus_space_write_2(va->va_memt, memh, RIEBL_IVEC_ADDR,
459 64 + 64);
460 break;
461 case LE_PAM:
462 bus_space_write_1(va->va_iot, ioh, LER_IVEC, 64 + 64);
463 break;
464 case LE_BVME410:
465 bus_space_write_2(va->va_iot, ioh, BVME410_IVEC, 64 + 64);
466 break;
467 }
468
469 /*
470 * Unmask the VME-interrupt we're on
471 */
472 if (machineid & ATARI_TT)
473 SCU->vme_mask |= 1 << va->va_irq;
474 }
475
476 /*
477 * True if 'addr' containe within [start,len]
478 */
479 #define WITHIN(start, len, addr) \
480 ((addr >= start) && ((addr) <= ((start) + (len))))
481 static void
482 riebl_skip_reserved_area(sc)
483 struct lance_softc *sc;
484 {
485 int offset = 0;
486 int i;
487
488 for(i = 0; i < sc->sc_nrbuf; i++) {
489 if (WITHIN(sc->sc_rbufaddr[i], LEBLEN, RIEBL_RES_START)
490 || WITHIN(sc->sc_rbufaddr[i], LEBLEN, RIEBL_RES_END)) {
491 offset = RIEBL_RES_END - sc->sc_rbufaddr[i];
492 }
493 sc->sc_rbufaddr[i] += offset;
494 }
495
496 for(i = 0; i < sc->sc_ntbuf; i++) {
497 if (WITHIN(sc->sc_tbufaddr[i], LEBLEN, RIEBL_RES_START)
498 || WITHIN(sc->sc_tbufaddr[i], LEBLEN, RIEBL_RES_END)) {
499 offset = RIEBL_RES_END - sc->sc_tbufaddr[i];
500 }
501 sc->sc_tbufaddr[i] += offset;
502 }
503 }
504
505 static int
506 nm93c06_read(iot, ioh, nm93c06reg)
507 bus_space_tag_t iot;
508 bus_space_handle_t ioh;
509 int nm93c06reg;
510 {
511 int bar;
512 int shift;
513 int bits = 0x180 | (nm93c06reg & 0xf);
514 int data = 0;
515
516 bar = 1<<BVME410_CS_SHIFT;
517 bus_space_write_2(iot, ioh, BVME410_BAR, bar);
518 delay(1); /* tCSS = 1 us */
519 for (shift = 9; shift >= 0; shift--) {
520 if (((bits >> shift) & 1) == 1)
521 bar |= 1<<BVME410_DIN_SHIFT;
522 else
523 bar &= ~(1<<BVME410_DIN_SHIFT);
524 bus_space_write_2(iot, ioh, BVME410_BAR, bar);
525 delay(1); /* tDIS = 0.4 us */
526 bar |= 1<<BVME410_CLK_SHIFT;
527 bus_space_write_2(iot, ioh, BVME410_BAR, bar);
528 delay(2); /* tSKH = 1 us, tSKH + tSKL >= 4 us */
529 bar &= ~(1<<BVME410_CLK_SHIFT);
530 bus_space_write_2(iot, ioh, BVME410_BAR, bar);
531 delay(2); /* tSKL = 1 us, tSKH + tSKL >= 4 us */
532 }
533 bar &= ~(1<<BVME410_DIN_SHIFT);
534 for (shift = 15; shift >= 0; shift--) {
535 delay(1); /* tDIS = 100 ns, BVM manual says 0.4 us */
536 bar |= 1<<BVME410_CLK_SHIFT;
537 bus_space_write_2(iot, ioh, BVME410_BAR, bar);
538 delay(2); /* tSKH = 1 us, tSKH + tSKL >= 4 us */
539 data |= (bus_space_read_2(iot, ioh, BVME410_BAR) & 1) << shift;
540 bar &= ~(1<<BVME410_CLK_SHIFT);
541 bus_space_write_2(iot, ioh, BVME410_BAR, bar);
542 delay(2); /* tSKL = 1 us, tSKH + tSKL >= 4 us */
543 }
544 bar &= ~(1<<BVME410_CS_SHIFT);
545 bus_space_write_2(iot, ioh, BVME410_BAR, bar);
546 delay(1); /* tCS = 1 us */
547 return data;
548 }
549
550 static int
551 bvme410_probe(iot, ioh)
552 bus_space_tag_t iot;
553 bus_space_handle_t ioh;
554 {
555 if (!bus_space_peek_2(iot, ioh, BVME410_IVEC))
556 return 0;
557
558 bus_space_write_2(iot, ioh, BVME410_IVEC, 0x0000);
559 if (bus_space_read_2(iot, ioh, BVME410_IVEC) != 0xff00)
560 return 0;
561
562 bus_space_write_2(iot, ioh, BVME410_IVEC, 0xffff);
563 if (bus_space_read_2(iot, ioh, BVME410_IVEC) != 0xffff)
564 return 0;
565
566 bus_space_write_2(iot, ioh, BVME410_IVEC, 0xa5a5);
567 if (bus_space_read_2(iot, ioh, BVME410_IVEC) != 0xffa5)
568 return 0;
569
570 return 1;
571 }
572
573 static int
574 bvme410_mem_size(memt, mem_addr)
575 bus_space_tag_t memt;
576 u_long mem_addr;
577 {
578 bus_space_handle_t memh;
579 int r;
580
581 if (bus_space_map(memt, mem_addr, 256*1024, 0, &memh))
582 return VMECF_MEMSIZ_DEFAULT;
583 if (!bus_space_peek_1(memt, memh, 0)) {
584 bus_space_unmap(memt, memh, 256*1024);
585 return VMECF_MEMSIZ_DEFAULT;
586 }
587 bus_space_write_1(memt, memh, 0, 128);
588 bus_space_write_1(memt, memh, 64*1024, 32);
589 bus_space_write_1(memt, memh, 32*1024, 8);
590 r = (int)(bus_space_read_1(memt, memh, 0) * 2048);
591 bus_space_unmap(memt, memh, 256*1024);
592 return r;
593 }
594
595 /*
596 * Need to be careful when writing to the bvme410 dual port memory.
597 * Continue writing each byte until it reads back the same.
598 */
599
600 static void
601 bvme410_copytobuf(sc, from, boff, len)
602 struct lance_softc *sc;
603 void *from;
604 int boff, len;
605 {
606 volatile char *buf = (volatile char *) sc->sc_mem;
607 char *f = (char *) from;
608
609 for (buf += boff; len; buf++,f++,len--)
610 do {
611 *buf = *f;
612 } while (*buf != *f);
613 }
614
615 static void
616 bvme410_zerobuf(sc, boff, len)
617 struct lance_softc *sc;
618 int boff, len;
619 {
620 volatile char *buf = (volatile char *)sc->sc_mem;
621
622 for (buf += boff; len; buf++,len--)
623 do {
624 *buf = '\0';
625 } while (*buf != '\0');
626 }
627
628