]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ed/if_ed_hpp.c
MFV r338866: 9700 ZFS resilvered mirror does not balance reads
[FreeBSD/FreeBSD.git] / sys / dev / ed / if_ed_hpp.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2005, M. Warner Losh
5  * All rights reserved.
6  * Copyright (c) 1995, David Greenman 
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice unmodified, this list of conditions, and the following
14  *    disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include "opt_ed.h"
36
37 #ifdef ED_HPP
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/sockio.h>
42 #include <sys/mbuf.h>
43 #include <sys/kernel.h>
44 #include <sys/socket.h>
45 #include <sys/syslog.h>
46
47 #include <sys/bus.h>
48
49 #include <machine/bus.h>
50 #include <sys/rman.h>
51 #include <machine/resource.h>
52
53 #include <net/ethernet.h>
54 #include <net/if.h>
55 #include <net/if_var.h>         /* XXX: ed_hpp_set_physical_link() */
56 #include <net/if_arp.h>
57 #include <net/if_dl.h>
58 #include <net/if_mib.h>
59 #include <net/if_media.h>
60
61 #include <net/bpf.h>
62
63 #include <dev/ed/if_edreg.h>
64 #include <dev/ed/if_edvar.h>
65
66 static void     ed_hpp_readmem(struct ed_softc *, bus_size_t, uint8_t *,
67                     uint16_t);
68 static void     ed_hpp_writemem(struct ed_softc *, uint8_t *, uint16_t,
69                     uint16_t);
70 static void     ed_hpp_set_physical_link(struct ed_softc *sc);
71 static u_short  ed_hpp_write_mbufs(struct ed_softc *, struct mbuf *,
72                     bus_size_t);
73
74 /*
75  * Interrupt conversion table for the HP PC LAN+
76  */
77 static uint16_t ed_hpp_intr_val[] = {
78         0,              /* 0 */
79         0,              /* 1 */
80         0,              /* 2 */
81         3,              /* 3 */
82         4,              /* 4 */
83         5,              /* 5 */
84         6,              /* 6 */
85         7,              /* 7 */
86         0,              /* 8 */
87         9,              /* 9 */
88         10,             /* 10 */
89         11,             /* 11 */
90         12,             /* 12 */
91         0,              /* 13 */
92         0,              /* 14 */
93         15              /* 15 */
94 };
95
96 #define ED_HPP_TEST_SIZE        16
97
98 /*
99  * Probe and vendor specific initialization for the HP PC Lan+ Cards.
100  * (HP Part nos: 27247B and 27252A).
101  *
102  * The card has an asic wrapper around a DS8390 core.  The asic handles 
103  * host accesses and offers both standard register IO and memory mapped 
104  * IO.  Memory mapped I/O allows better performance at the expense of greater
105  * chance of an incompatibility with existing ISA cards.
106  *
107  * The card has a few caveats: it isn't tolerant of byte wide accesses, only
108  * short (16 bit) or word (32 bit) accesses are allowed.  Some card revisions
109  * don't allow 32 bit accesses; these are indicated by a bit in the software
110  * ID register (see if_edreg.h).
111  * 
112  * Other caveats are: we should read the MAC address only when the card
113  * is inactive.
114  *
115  * For more information; please consult the CRYNWR packet driver.
116  *
117  * The AUI port is turned on using the "link2" option on the ifconfig 
118  * command line.
119  */
120 int
121 ed_probe_HP_pclanp(device_t dev, int port_rid, int flags)
122 {
123         struct ed_softc *sc = device_get_softc(dev);
124         int error;
125         int n;                          /* temp var */
126         int memsize;                    /* mem on board */
127         u_char checksum;                /* checksum of board address */
128         u_char irq;                     /* board configured IRQ */
129         uint8_t test_pattern[ED_HPP_TEST_SIZE]; /* read/write areas for */
130         uint8_t test_buffer[ED_HPP_TEST_SIZE];  /* probing card */
131         rman_res_t conf_maddr, conf_msize, conf_irq, junk;
132
133         error = ed_alloc_port(dev, 0, ED_HPP_IO_PORTS);
134         if (error)
135                 return (error);
136
137         /* Fill in basic information */
138         sc->asic_offset = ED_HPP_ASIC_OFFSET;
139         sc->nic_offset  = ED_HPP_NIC_OFFSET;
140
141         sc->chip_type = ED_CHIP_TYPE_DP8390;
142         sc->isa16bit = 0;       /* the 8390 core needs to be in byte mode */
143
144         /* 
145          * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53" 
146          */
147         
148         if ((ed_asic_inb(sc, ED_HPP_ID) != 0x50) || 
149             (ed_asic_inb(sc, ED_HPP_ID + 1) != 0x48) ||
150             ((ed_asic_inb(sc, ED_HPP_ID + 2) & 0xF0) != 0) ||
151             (ed_asic_inb(sc, ED_HPP_ID + 3) != 0x53))
152                 return (ENXIO);
153
154         /* 
155          * Read the MAC address and verify checksum on the address.
156          */
157
158         ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_MAC);
159         for (n  = 0, checksum = 0; n < ETHER_ADDR_LEN; n++)
160                 checksum += (sc->enaddr[n] = 
161                     ed_asic_inb(sc, ED_HPP_MAC_ADDR + n));
162         
163         checksum += ed_asic_inb(sc, ED_HPP_MAC_ADDR + ETHER_ADDR_LEN);
164
165         if (checksum != 0xFF)
166                 return (ENXIO);
167
168         /*
169          * Verify that the software model number is 0.
170          */
171         
172         ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_ID);
173         if (((sc->hpp_id = ed_asic_inw(sc, ED_HPP_PAGE_4)) & 
174                 ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000)
175                 return (ENXIO);
176
177         /*
178          * Read in and save the current options configured on card.
179          */
180
181         sc->hpp_options = ed_asic_inw(sc, ED_HPP_OPTION);
182
183         sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET | 
184             ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ);
185
186         /* 
187          * Reset the chip.  This requires writing to the option register
188          * so take care to preserve the other bits.
189          */
190
191         ed_asic_outw(sc, ED_HPP_OPTION, 
192             (sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET | 
193             ED_HPP_OPTION_CHIP_RESET)));
194
195         DELAY(5000);    /* wait for chip reset to complete */
196
197         ed_asic_outw(sc, ED_HPP_OPTION,
198             (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET |
199             ED_HPP_OPTION_CHIP_RESET |
200             ED_HPP_OPTION_ENABLE_IRQ)));
201
202         DELAY(5000);
203
204         if (!(ed_nic_inb(sc, ED_P0_ISR) & ED_ISR_RST))
205                 return (ENXIO); /* reset did not complete */
206
207         /*
208          * Read out configuration information.
209          */
210
211         ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
212
213         irq = ed_asic_inb(sc, ED_HPP_HW_IRQ);
214
215         /*
216          * Check for impossible IRQ.
217          */
218
219         if (irq >= nitems(ed_hpp_intr_val))
220                 return (ENXIO);
221
222         /* 
223          * If the kernel IRQ was specified with a '?' use the cards idea
224          * of the IRQ.  If the kernel IRQ was explicitly specified, it
225          * should match that of the hardware.
226          */
227         error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
228         if (error)
229                 bus_set_resource(dev, SYS_RES_IRQ, 0, ed_hpp_intr_val[irq], 1);
230         else {
231                 if (conf_irq != ed_hpp_intr_val[irq])
232                         return (ENXIO);
233         }
234
235         /*
236          * Fill in softconfig info.
237          */
238
239         sc->vendor = ED_VENDOR_HP;
240         sc->type = ED_TYPE_HP_PCLANPLUS;
241         sc->type_str = "HP-PCLAN+";
242
243         sc->mem_shared = 0;     /* we DON'T have dual ported RAM */
244         sc->mem_start = 0;      /* we use offsets inside the card RAM */
245
246         sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */
247
248         /*
249          * The board has 32KB of memory.  Is there a way to determine
250          * this programmatically?
251          */
252         
253         memsize = 32768;
254
255         /*
256          * Check if memory mapping of the I/O registers possible.
257          */
258         if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) {
259                 u_long mem_addr;
260
261                 /*
262                  * determine the memory address from the board.
263                  */
264                 
265                 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
266                 mem_addr = (ed_asic_inw(sc, ED_HPP_HW_MEM_MAP) << 8);
267
268                 /*
269                  * Check that the kernel specified start of memory and
270                  * hardware's idea of it match.
271                  */
272                 error = bus_get_resource(dev, SYS_RES_MEMORY, 0,
273                                          &conf_maddr, &conf_msize);
274                 if (error)
275                         return (error);
276                 
277                 if (mem_addr != conf_maddr)
278                         return (ENXIO);
279
280                 error = ed_alloc_memory(dev, 0, memsize);
281                 if (error)
282                         return (error);
283
284                 sc->hpp_mem_start = rman_get_virtual(sc->mem_res);
285         }
286
287         /*
288          * Fill in the rest of the soft config structure.
289          */
290
291         /*
292          * The transmit page index.
293          */
294
295         sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET;
296
297         if (device_get_flags(dev) & ED_FLAGS_NO_MULTI_BUFFERING)
298                 sc->txb_cnt = 1;
299         else
300                 sc->txb_cnt = 2;
301
302         /*
303          * Memory description
304          */
305
306         sc->mem_size = memsize;
307         sc->mem_ring = sc->mem_start + 
308                 (sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE);
309         sc->mem_end = sc->mem_start + sc->mem_size;
310
311         /*
312          * Receive area starts after the transmit area and 
313          * continues till the end of memory.
314          */
315
316         sc->rec_page_start = sc->tx_page_start + 
317                                 (sc->txb_cnt * ED_TXBUF_SIZE);
318         sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE);
319
320
321         sc->cr_proto = 0;       /* value works */
322
323         /*
324          * Set the wrap registers for string I/O reads.
325          */
326
327         ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
328         ed_asic_outw(sc, ED_HPP_HW_WRAP,
329             ((sc->rec_page_start / ED_PAGE_SIZE) |
330             (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8)));
331
332         /*
333          * Reset the register page to normal operation.
334          */
335
336         ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
337
338         /*
339          * Verify that we can read/write from adapter memory.
340          * Create test pattern.
341          */
342
343         for (n = 0; n < ED_HPP_TEST_SIZE; n++)
344                 test_pattern[n] = (n*n) ^ ~n;
345
346 #undef  ED_HPP_TEST_SIZE
347
348         /*
349          * Check that the memory is accessible thru the I/O ports.
350          * Write out the contents of "test_pattern", read back
351          * into "test_buffer" and compare the two for any
352          * mismatch.
353          */
354
355         for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) {
356                 ed_hpp_writemem(sc, test_pattern, (n * ED_PAGE_SIZE), 
357                                 sizeof(test_pattern));
358                 ed_hpp_readmem(sc, (n * ED_PAGE_SIZE), 
359                         test_buffer, sizeof(test_pattern));
360
361                 if (bcmp(test_pattern, test_buffer, 
362                         sizeof(test_pattern)))
363                         return (ENXIO);
364         }
365
366         sc->sc_mediachg = ed_hpp_set_physical_link;
367         sc->sc_write_mbufs = ed_hpp_write_mbufs;
368         sc->readmem = ed_hpp_readmem;
369         return (0);
370 }
371
372 /*
373  * HP PC Lan+ : Set the physical link to use AUI or TP/TL.
374  */
375
376 static void
377 ed_hpp_set_physical_link(struct ed_softc *sc)
378 {
379         struct ifnet *ifp = sc->ifp;
380         int lan_page;
381
382         ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
383         lan_page = ed_asic_inw(sc, ED_HPP_PAGE_0);
384
385         if (ifp->if_flags & IFF_LINK2) {
386                 /*
387                  * Use the AUI port.
388                  */
389
390                 lan_page |= ED_HPP_LAN_AUI;
391                 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
392                 ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
393         } else {
394                 /*
395                  * Use the ThinLan interface
396                  */
397
398                 lan_page &= ~ED_HPP_LAN_AUI;
399                 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
400                 ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
401         }
402
403         /*
404          * Wait for the lan card to re-initialize itself
405          */
406         DELAY(150000);  /* wait 150 ms */
407
408         /*
409          * Restore normal pages.
410          */
411         ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
412 }
413
414 /*
415  * Support routines to handle the HP PC Lan+ card.
416  */
417
418 /*
419  * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped
420  * IO.
421  */
422
423 static void
424 ed_hpp_readmem(struct ed_softc *sc, bus_size_t src, uint8_t *dst,
425     uint16_t amount)
426 {
427         int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
428
429         /* Program the source address in RAM */
430         ed_asic_outw(sc, ED_HPP_PAGE_2, src);
431
432         /*
433          * The HP PC Lan+ card supports word reads as well as
434          * a memory mapped i/o port that is aliased to every 
435          * even address on the board.
436          */
437         if (sc->hpp_mem_start) {
438                 /* Enable memory mapped access.  */
439                 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options & 
440                         ~(ED_HPP_OPTION_MEM_DISABLE | 
441                           ED_HPP_OPTION_BOOT_ROM_ENB));
442
443                 if (use_32bit_access && (amount > 3)) {
444                         uint32_t *dl = (uint32_t *) dst;        
445                         volatile uint32_t *const sl = 
446                                 (uint32_t *) sc->hpp_mem_start;
447                         uint32_t *const fence = dl + (amount >> 2);
448                         
449                         /*
450                          * Copy out NIC data.  We could probably write this
451                          * as a `movsl'. The currently generated code is lousy.
452                          */
453                         while (dl < fence)
454                                 *dl++ = *sl;
455                 
456                         dst += (amount & ~3);
457                         amount &= 3;
458
459                 } 
460
461                 /* Finish off any words left, as a series of short reads */
462                 if (amount > 1) {
463                         u_short *d = (u_short *) dst;   
464                         volatile u_short *const s = 
465                                 (u_short *) sc->hpp_mem_start;
466                         u_short *const fence = d + (amount >> 1);
467                         
468                         /* Copy out NIC data.  */
469                         while (d < fence)
470                                 *d++ = *s;
471         
472                         dst += (amount & ~1);
473                         amount &= 1;
474                 }
475
476                 /*
477                  * read in a byte; however we need to always read 16 bits
478                  * at a time or the hardware gets into a funny state
479                  */
480
481                 if (amount == 1) {
482                         /* need to read in a short and copy LSB */
483                         volatile u_short *const s = 
484                                 (volatile u_short *) sc->hpp_mem_start;
485                         *dst = (*s) & 0xFF;     
486                 }
487
488                 /* Restore Boot ROM access.  */
489                 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
490         } else { 
491                 /* Read in data using the I/O port */
492                 if (use_32bit_access && (amount > 3)) {
493                         ed_asic_insl(sc, ED_HPP_PAGE_4, dst, amount >> 2);
494                         dst += (amount & ~3);
495                         amount &= 3;
496                 }
497                 if (amount > 1) {
498                         ed_asic_insw(sc, ED_HPP_PAGE_4, dst, amount >> 1);
499                         dst += (amount & ~1);
500                         amount &= 1;
501                 }
502                 if (amount == 1) { /* read in a short and keep the LSB */
503                         *dst = ed_asic_inw(sc, ED_HPP_PAGE_4) & 0xFF;
504                 }
505         }
506 }
507
508 /*
509  * HP PC Lan+: Write to NIC memory, using either PIO or memory mapped
510  * IO.
511  *      Only used in the probe routine to test the memory. 'len' must
512  *      be even.
513  */
514 static void
515 ed_hpp_writemem(struct ed_softc *sc, uint8_t *src, uint16_t dst, uint16_t len)
516 {
517         /* reset remote DMA complete flag */
518         ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
519
520         /* program the write address in RAM */
521         ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
522
523         if (sc->hpp_mem_start) {
524                 u_short *s = (u_short *) src;
525                 volatile u_short *d = (u_short *) sc->hpp_mem_start;
526                 u_short *const fence = s + (len >> 1);
527
528                 /*
529                  * Enable memory mapped access.
530                  */
531                 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options & 
532                         ~(ED_HPP_OPTION_MEM_DISABLE | 
533                           ED_HPP_OPTION_BOOT_ROM_ENB));
534
535                 /*
536                  * Copy to NIC memory.
537                  */
538                 while (s < fence)
539                         *d = *s++;
540
541                 /*
542                  * Restore Boot ROM access.
543                  */
544                 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
545         } else {
546                 /* write data using I/O writes */
547                 ed_asic_outsw(sc, ED_HPP_PAGE_4, src, len / 2);
548         }
549 }
550
551 /*
552  * Write to HP PC Lan+ NIC memory.  Access to the NIC can be by using 
553  * outsw() or via the memory mapped interface to the same register.
554  * Writes have to be in word units; byte accesses won't work and may cause
555  * the NIC to behave weirdly. Long word accesses are permitted if the ASIC
556  * allows it.
557  */
558
559 static u_short
560 ed_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, bus_size_t dst)
561 {
562         int len, wantbyte;
563         unsigned short total_len;
564         unsigned char savebyte[2];
565         volatile u_short * const d = 
566                 (volatile u_short *) sc->hpp_mem_start;
567         int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
568
569         /* select page 0 registers */
570         ed_nic_barrier(sc, ED_P0_CR, 1,
571             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
572         ed_nic_outb(sc, ED_P0_CR, sc->cr_proto | ED_CR_STA);
573         ed_nic_barrier(sc, ED_P0_CR, 1,
574             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
575
576         /* reset remote DMA complete flag */
577         ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
578
579         /* program the write address in RAM */
580         ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
581
582         if (sc->hpp_mem_start)  /* enable memory mapped I/O */
583                 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options & 
584                         ~(ED_HPP_OPTION_MEM_DISABLE |
585                         ED_HPP_OPTION_BOOT_ROM_ENB));
586
587         wantbyte = 0;
588         total_len = 0;
589
590         if (sc->hpp_mem_start) {        /* Memory mapped I/O port */
591                 while (m) {
592                         total_len += (len = m->m_len);
593                         if (len) {
594                                 caddr_t data = mtod(m, caddr_t);
595                                 /* finish the last word of the previous mbuf */
596                                 if (wantbyte) {
597                                         savebyte[1] = *data;
598                                         *d = *((u_short *) savebyte);
599                                         data++; len--; wantbyte = 0;
600                                 }
601                                 /* output contiguous words */
602                                 if ((len > 3) && (use_32bit_accesses)) {
603                                         volatile uint32_t *const dl = 
604                                                 (volatile uint32_t *) d;
605                                         uint32_t *sl = (uint32_t *) data;
606                                         uint32_t *fence = sl + (len >> 2);
607
608                                         while (sl < fence)
609                                                 *dl = *sl++;
610
611                                         data += (len & ~3);
612                                         len &= 3;
613                                 }
614                                 /* finish off remain 16 bit writes */
615                                 if (len > 1) {
616                                         u_short *s = (u_short *) data;
617                                         u_short *fence = s + (len >> 1);
618
619                                         while (s < fence)
620                                                 *d = *s++;
621
622                                         data += (len & ~1); 
623                                         len &= 1;
624                                 }
625                                 /* save last byte if needed */
626                                 if ((wantbyte = (len == 1)) != 0)
627                                         savebyte[0] = *data;
628                         }
629                         m = m->m_next;  /* to next mbuf */
630                 }
631                 if (wantbyte) /* write last byte */
632                         *d = *((u_short *) savebyte);
633         } else {
634                 /* use programmed I/O */
635                 while (m) {
636                         total_len += (len = m->m_len);
637                         if (len) {
638                                 caddr_t data = mtod(m, caddr_t);
639                                 /* finish the last word of the previous mbuf */
640                                 if (wantbyte) {
641                                         savebyte[1] = *data;
642                                         ed_asic_outw(sc, ED_HPP_PAGE_4,
643                                                      *((u_short *)savebyte));
644                                         data++; 
645                                         len--; 
646                                         wantbyte = 0;
647                                 }
648                                 /* output contiguous words */
649                                 if ((len > 3) && use_32bit_accesses) {
650                                         ed_asic_outsl(sc, ED_HPP_PAGE_4,
651                                                       data, len >> 2);
652                                         data += (len & ~3);
653                                         len &= 3;
654                                 }
655                                 /* finish off remaining 16 bit accesses */
656                                 if (len > 1) {
657                                         ed_asic_outsw(sc, ED_HPP_PAGE_4,
658                                                       data, len >> 1);
659                                         data += (len & ~1);
660                                         len &= 1;
661                                 }
662                                 if ((wantbyte = (len == 1)) != 0)
663                                         savebyte[0] = *data;
664
665                         } /* if len != 0 */
666                         m = m->m_next;
667                 }
668                 if (wantbyte) /* spit last byte */
669                         ed_asic_outw(sc, ED_HPP_PAGE_4, *(u_short *)savebyte);
670
671         }
672
673         if (sc->hpp_mem_start)  /* turn off memory mapped i/o */
674                 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
675
676         return (total_len);
677 }
678
679 #endif /* ED_HPP */