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