if_ed_hpp.c revision 154892
1141586Simp/*-
2141586Simp * Copyright (c) 2005, M. Warner Losh
3141586Simp * All rights reserved.
4141586Simp * Copyright (c) 1995, David Greenman
5141586Simp * All rights reserved.
6141586Simp *
7141586Simp * Redistribution and use in source and binary forms, with or without
8141586Simp * modification, are permitted provided that the following conditions
9141586Simp * are met:
10141586Simp * 1. Redistributions of source code must retain the above copyright
11141586Simp *    notice unmodified, this list of conditions, and the following
12141586Simp *    disclaimer.
13141586Simp * 2. Redistributions in binary form must reproduce the above copyright
14141586Simp *    notice, this list of conditions and the following disclaimer in the
15141586Simp *    documentation and/or other materials provided with the distribution.
16141586Simp *
17141586Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18141586Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19141586Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20141586Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21141586Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22141586Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23141586Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24141586Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25141586Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26141586Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27141586Simp * SUCH DAMAGE.
28141586Simp */
29141586Simp
30141586Simp#include <sys/cdefs.h>
31141586Simp__FBSDID("$FreeBSD: head/sys/dev/ed/if_ed_hpp.c 154892 2006-01-27 08:00:40Z imp $");
32141586Simp
33141586Simp#include "opt_ed.h"
34141586Simp
35141586Simp#ifdef ED_HPP
36141586Simp
37141586Simp#include <sys/param.h>
38141586Simp#include <sys/systm.h>
39141586Simp#include <sys/sockio.h>
40141586Simp#include <sys/mbuf.h>
41141586Simp#include <sys/kernel.h>
42141586Simp#include <sys/socket.h>
43141586Simp#include <sys/syslog.h>
44141586Simp
45141586Simp#include <sys/bus.h>
46141586Simp
47141586Simp#include <machine/bus.h>
48141586Simp#include <sys/rman.h>
49141586Simp#include <machine/resource.h>
50141586Simp
51141586Simp#include <net/ethernet.h>
52141586Simp#include <net/if.h>
53141586Simp#include <net/if_arp.h>
54141586Simp#include <net/if_dl.h>
55141586Simp#include <net/if_mib.h>
56141586Simp#include <net/if_media.h>
57141586Simp
58141586Simp#include <net/bpf.h>
59141586Simp
60141586Simp#include <dev/ed/if_edreg.h>
61141586Simp#include <dev/ed/if_edvar.h>
62141586Simp
63141586Simpstatic void	ed_hpp_writemem(struct ed_softc *, uint8_t *, uint16_t,
64141586Simp		    uint16_t);
65141586Simp
66141586Simp/*
67141586Simp * Interrupt conversion table for the HP PC LAN+
68141586Simp */
69141586Simpstatic uint16_t ed_hpp_intr_val[] = {
70141586Simp	0,		/* 0 */
71141586Simp	0,		/* 1 */
72141586Simp	0,		/* 2 */
73141586Simp	3,		/* 3 */
74141586Simp	4,		/* 4 */
75141586Simp	5,		/* 5 */
76141586Simp	6,		/* 6 */
77141586Simp	7,		/* 7 */
78141586Simp	0,		/* 8 */
79141586Simp	9,		/* 9 */
80141586Simp	10,		/* 10 */
81141586Simp	11,		/* 11 */
82141586Simp	12,		/* 12 */
83141586Simp	0,		/* 13 */
84141586Simp	0,		/* 14 */
85141586Simp	15		/* 15 */
86141586Simp};
87141586Simp
88141586Simp#define	ED_HPP_TEST_SIZE	16
89141586Simp
90141586Simp/*
91141586Simp * Probe and vendor specific initialization for the HP PC Lan+ Cards.
92141586Simp * (HP Part nos: 27247B and 27252A).
93141586Simp *
94141586Simp * The card has an asic wrapper around a DS8390 core.  The asic handles
95141586Simp * host accesses and offers both standard register IO and memory mapped
96141586Simp * IO.  Memory mapped I/O allows better performance at the expense of greater
97141586Simp * chance of an incompatibility with existing ISA cards.
98141586Simp *
99141586Simp * The card has a few caveats: it isn't tolerant of byte wide accesses, only
100141586Simp * short (16 bit) or word (32 bit) accesses are allowed.  Some card revisions
101141586Simp * don't allow 32 bit accesses; these are indicated by a bit in the software
102141586Simp * ID register (see if_edreg.h).
103141586Simp *
104141586Simp * Other caveats are: we should read the MAC address only when the card
105141586Simp * is inactive.
106141586Simp *
107141586Simp * For more information; please consult the CRYNWR packet driver.
108141586Simp *
109141586Simp * The AUI port is turned on using the "link2" option on the ifconfig
110141586Simp * command line.
111141586Simp */
112141586Simpint
113141586Simped_probe_HP_pclanp(device_t dev, int port_rid, int flags)
114141586Simp{
115141586Simp	struct ed_softc *sc = device_get_softc(dev);
116141586Simp	int error;
117141586Simp	int n;				/* temp var */
118141586Simp	int memsize;			/* mem on board */
119141586Simp	u_char checksum;		/* checksum of board address */
120141586Simp	u_char irq;			/* board configured IRQ */
121141586Simp	uint8_t test_pattern[ED_HPP_TEST_SIZE];	/* read/write areas for */
122141586Simp	uint8_t test_buffer[ED_HPP_TEST_SIZE];	/* probing card */
123141586Simp	u_long conf_maddr, conf_msize, conf_irq, junk;
124141586Simp
125141586Simp	error = ed_alloc_port(dev, 0, ED_HPP_IO_PORTS);
126141586Simp	if (error)
127141586Simp		return (error);
128141586Simp
129141586Simp	/* Fill in basic information */
130141586Simp	sc->asic_offset = ED_HPP_ASIC_OFFSET;
131141586Simp	sc->nic_offset  = ED_HPP_NIC_OFFSET;
132141586Simp
133141586Simp	sc->chip_type = ED_CHIP_TYPE_DP8390;
134141586Simp	sc->isa16bit = 0;	/* the 8390 core needs to be in byte mode */
135141586Simp
136141586Simp	/*
137141586Simp	 * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53"
138141586Simp	 */
139141586Simp
140141586Simp	if ((ed_asic_inb(sc, ED_HPP_ID) != 0x50) ||
141141586Simp	    (ed_asic_inb(sc, ED_HPP_ID + 1) != 0x48) ||
142141586Simp	    ((ed_asic_inb(sc, ED_HPP_ID + 2) & 0xF0) != 0) ||
143141586Simp	    (ed_asic_inb(sc, ED_HPP_ID + 3) != 0x53))
144141586Simp		return ENXIO;
145141586Simp
146141586Simp	/*
147141586Simp	 * Read the MAC address and verify checksum on the address.
148141586Simp	 */
149141586Simp
150141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_MAC);
151141586Simp	for (n  = 0, checksum = 0; n < ETHER_ADDR_LEN; n++)
152147256Sbrooks		checksum += (sc->enaddr[n] =
153141586Simp		    ed_asic_inb(sc, ED_HPP_MAC_ADDR + n));
154141586Simp
155141586Simp	checksum += ed_asic_inb(sc, ED_HPP_MAC_ADDR + ETHER_ADDR_LEN);
156141586Simp
157141586Simp	if (checksum != 0xFF)
158141586Simp		return ENXIO;
159141586Simp
160141586Simp	/*
161141586Simp	 * Verify that the software model number is 0.
162141586Simp	 */
163141586Simp
164141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_ID);
165141586Simp	if (((sc->hpp_id = ed_asic_inw(sc, ED_HPP_PAGE_4)) &
166141586Simp		ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000)
167141586Simp		return ENXIO;
168141586Simp
169141586Simp	/*
170141586Simp	 * Read in and save the current options configured on card.
171141586Simp	 */
172141586Simp
173141586Simp	sc->hpp_options = ed_asic_inw(sc, ED_HPP_OPTION);
174141586Simp
175141586Simp	sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET |
176141586Simp	    ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ);
177141586Simp
178141586Simp	/*
179141586Simp	 * Reset the chip.  This requires writing to the option register
180141586Simp	 * so take care to preserve the other bits.
181141586Simp	 */
182141586Simp
183141586Simp	ed_asic_outw(sc, ED_HPP_OPTION,
184141586Simp	    (sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET |
185141586Simp	    ED_HPP_OPTION_CHIP_RESET)));
186141586Simp
187141586Simp	DELAY(5000);	/* wait for chip reset to complete */
188141586Simp
189141586Simp	ed_asic_outw(sc, ED_HPP_OPTION,
190141586Simp	    (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET |
191141586Simp	    ED_HPP_OPTION_CHIP_RESET |
192141586Simp	    ED_HPP_OPTION_ENABLE_IRQ)));
193141586Simp
194141586Simp	DELAY(5000);
195141586Simp
196141586Simp	if (!(ed_nic_inb(sc, ED_P0_ISR) & ED_ISR_RST))
197141586Simp		return ENXIO;	/* reset did not complete */
198141586Simp
199141586Simp	/*
200141586Simp	 * Read out configuration information.
201141586Simp	 */
202141586Simp
203141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
204141586Simp
205141586Simp	irq = ed_asic_inb(sc, ED_HPP_HW_IRQ);
206141586Simp
207141586Simp	/*
208141586Simp 	 * Check for impossible IRQ.
209141586Simp	 */
210141586Simp
211141586Simp	if (irq >= (sizeof(ed_hpp_intr_val) / sizeof(ed_hpp_intr_val[0])))
212141586Simp		return ENXIO;
213141586Simp
214141586Simp	/*
215141586Simp	 * If the kernel IRQ was specified with a '?' use the cards idea
216141586Simp	 * of the IRQ.  If the kernel IRQ was explicitly specified, it
217141586Simp 	 * should match that of the hardware.
218141586Simp	 */
219141586Simp	error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
220141586Simp	if (error)
221141586Simp		bus_set_resource(dev, SYS_RES_IRQ, 0, ed_hpp_intr_val[irq], 1);
222141586Simp	else {
223141586Simp		if (conf_irq != ed_hpp_intr_val[irq])
224141586Simp			return (ENXIO);
225141586Simp	}
226141586Simp
227141586Simp	/*
228141586Simp	 * Fill in softconfig info.
229141586Simp	 */
230141586Simp
231141586Simp	sc->vendor = ED_VENDOR_HP;
232141586Simp	sc->type = ED_TYPE_HP_PCLANPLUS;
233141586Simp	sc->type_str = "HP-PCLAN+";
234141586Simp
235141586Simp	sc->mem_shared = 0;	/* we DON'T have dual ported RAM */
236141586Simp	sc->mem_start = 0;	/* we use offsets inside the card RAM */
237141586Simp
238141586Simp	sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */
239141586Simp
240141586Simp	/*
241141586Simp	 * The board has 32KB of memory.  Is there a way to determine
242141586Simp	 * this programmatically?
243141586Simp	 */
244141586Simp
245141586Simp	memsize = 32768;
246141586Simp
247141586Simp	/*
248141586Simp	 * Check if memory mapping of the I/O registers possible.
249141586Simp	 */
250141586Simp	if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) {
251141586Simp		u_long mem_addr;
252141586Simp
253141586Simp		/*
254141586Simp		 * determine the memory address from the board.
255141586Simp		 */
256141586Simp
257141586Simp		ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
258141586Simp		mem_addr = (ed_asic_inw(sc, ED_HPP_HW_MEM_MAP) << 8);
259141586Simp
260141586Simp		/*
261141586Simp		 * Check that the kernel specified start of memory and
262141586Simp		 * hardware's idea of it match.
263141586Simp		 */
264141586Simp		error = bus_get_resource(dev, SYS_RES_MEMORY, 0,
265141586Simp					 &conf_maddr, &conf_msize);
266141586Simp		if (error)
267141586Simp			return (error);
268141586Simp
269141586Simp		if (mem_addr != conf_maddr)
270141586Simp			return ENXIO;
271141586Simp
272141586Simp		error = ed_alloc_memory(dev, 0, memsize);
273141586Simp		if (error)
274141586Simp			return (error);
275141586Simp
276141586Simp		sc->hpp_mem_start = rman_get_virtual(sc->mem_res);
277141586Simp	}
278141586Simp
279141586Simp	/*
280141586Simp	 * Fill in the rest of the soft config structure.
281141586Simp	 */
282141586Simp
283141586Simp	/*
284141586Simp	 * The transmit page index.
285141586Simp	 */
286141586Simp
287141586Simp	sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET;
288141586Simp
289141586Simp	if (device_get_flags(dev) & ED_FLAGS_NO_MULTI_BUFFERING)
290141586Simp		sc->txb_cnt = 1;
291141586Simp	else
292141586Simp		sc->txb_cnt = 2;
293141586Simp
294141586Simp	/*
295141586Simp	 * Memory description
296141586Simp	 */
297141586Simp
298141586Simp	sc->mem_size = memsize;
299141586Simp	sc->mem_ring = sc->mem_start +
300141586Simp		(sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE);
301141586Simp	sc->mem_end = sc->mem_start + sc->mem_size;
302141586Simp
303141586Simp	/*
304141586Simp	 * Receive area starts after the transmit area and
305141586Simp	 * continues till the end of memory.
306141586Simp	 */
307141586Simp
308141586Simp	sc->rec_page_start = sc->tx_page_start +
309141586Simp				(sc->txb_cnt * ED_TXBUF_SIZE);
310141586Simp	sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE);
311141586Simp
312141586Simp
313141586Simp	sc->cr_proto = 0;	/* value works */
314141586Simp
315141586Simp	/*
316141586Simp	 * Set the wrap registers for string I/O reads.
317141586Simp	 */
318141586Simp
319141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
320141586Simp	ed_asic_outw(sc, ED_HPP_HW_WRAP,
321141586Simp	    ((sc->rec_page_start / ED_PAGE_SIZE) |
322141586Simp	    (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8)));
323141586Simp
324141586Simp	/*
325141586Simp	 * Reset the register page to normal operation.
326141586Simp	 */
327141586Simp
328141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
329141586Simp
330141586Simp	/*
331141586Simp	 * Verify that we can read/write from adapter memory.
332141586Simp	 * Create test pattern.
333141586Simp	 */
334141586Simp
335141586Simp	for (n = 0; n < ED_HPP_TEST_SIZE; n++)
336141586Simp		test_pattern[n] = (n*n) ^ ~n;
337141586Simp
338141586Simp#undef	ED_HPP_TEST_SIZE
339141586Simp
340141586Simp	/*
341141586Simp	 * Check that the memory is accessible thru the I/O ports.
342141586Simp	 * Write out the contents of "test_pattern", read back
343141586Simp	 * into "test_buffer" and compare the two for any
344141586Simp	 * mismatch.
345141586Simp	 */
346141586Simp
347141586Simp	for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) {
348141586Simp		ed_hpp_writemem(sc, test_pattern, (n * ED_PAGE_SIZE),
349141586Simp				sizeof(test_pattern));
350141586Simp		ed_hpp_readmem(sc, (n * ED_PAGE_SIZE),
351141586Simp			test_buffer, sizeof(test_pattern));
352141586Simp
353141586Simp		if (bcmp(test_pattern, test_buffer,
354141586Simp			sizeof(test_pattern)))
355141586Simp			return ENXIO;
356141586Simp	}
357141586Simp
358141586Simp	return (0);
359141586Simp}
360141586Simp
361141586Simp/*
362141586Simp * HP PC Lan+ : Set the physical link to use AUI or TP/TL.
363141586Simp */
364141586Simp
365141586Simpvoid
366141586Simped_hpp_set_physical_link(struct ed_softc *sc)
367141586Simp{
368147256Sbrooks	struct ifnet *ifp = sc->ifp;
369141586Simp	int lan_page;
370141586Simp
371141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
372141586Simp	lan_page = ed_asic_inw(sc, ED_HPP_PAGE_0);
373141586Simp
374154892Simp	if (ifp->if_flags & IFF_LINK2) {
375141586Simp		/*
376141586Simp		 * Use the AUI port.
377141586Simp		 */
378141586Simp
379141586Simp		lan_page |= ED_HPP_LAN_AUI;
380141586Simp		ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
381141586Simp		ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
382141586Simp	} else {
383141586Simp		/*
384141586Simp		 * Use the ThinLan interface
385141586Simp		 */
386141586Simp
387141586Simp		lan_page &= ~ED_HPP_LAN_AUI;
388141586Simp		ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
389141586Simp		ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
390141586Simp	}
391141586Simp
392141586Simp	/*
393141586Simp	 * Wait for the lan card to re-initialize itself
394141586Simp	 */
395141586Simp	DELAY(150000);	/* wait 150 ms */
396141586Simp
397141586Simp	/*
398141586Simp	 * Restore normal pages.
399141586Simp	 */
400141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
401141586Simp}
402141586Simp
403141586Simp/*
404141586Simp * Support routines to handle the HP PC Lan+ card.
405141586Simp */
406141586Simp
407141586Simp/*
408141586Simp * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped
409141586Simp * IO.
410141586Simp */
411141586Simp
412141586Simpvoid
413149558Simped_hpp_readmem(struct ed_softc *sc, bus_size_t src, uint8_t *dst,
414149558Simp    uint16_t amount)
415141586Simp{
416141586Simp	int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
417141586Simp
418141586Simp	/* Program the source address in RAM */
419141586Simp	ed_asic_outw(sc, ED_HPP_PAGE_2, src);
420141586Simp
421141586Simp	/*
422141586Simp	 * The HP PC Lan+ card supports word reads as well as
423141586Simp	 * a memory mapped i/o port that is aliased to every
424141586Simp	 * even address on the board.
425141586Simp	 */
426141586Simp	if (sc->hpp_mem_start) {
427141586Simp		/* Enable memory mapped access.  */
428141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
429141586Simp			~(ED_HPP_OPTION_MEM_DISABLE |
430141586Simp			  ED_HPP_OPTION_BOOT_ROM_ENB));
431141586Simp
432141586Simp		if (use_32bit_access && (amount > 3)) {
433141586Simp			uint32_t *dl = (uint32_t *) dst;
434141586Simp			volatile uint32_t *const sl =
435141586Simp				(uint32_t *) sc->hpp_mem_start;
436141586Simp			uint32_t *const fence = dl + (amount >> 2);
437141586Simp
438149558Simp			/*
439149558Simp			 * Copy out NIC data.  We could probably write this
440149558Simp			 * as a `movsl'. The currently generated code is lousy.
441149558Simp			 */
442141586Simp			while (dl < fence)
443141586Simp				*dl++ = *sl;
444141586Simp
445141586Simp			dst += (amount & ~3);
446141586Simp			amount &= 3;
447141586Simp
448141586Simp		}
449141586Simp
450141586Simp		/* Finish off any words left, as a series of short reads */
451141586Simp		if (amount > 1) {
452141586Simp			u_short *d = (u_short *) dst;
453141586Simp			volatile u_short *const s =
454141586Simp				(u_short *) sc->hpp_mem_start;
455141586Simp			u_short *const fence = d + (amount >> 1);
456141586Simp
457141586Simp			/* Copy out NIC data.  */
458141586Simp			while (d < fence)
459141586Simp				*d++ = *s;
460141586Simp
461141586Simp			dst += (amount & ~1);
462141586Simp			amount &= 1;
463141586Simp		}
464141586Simp
465141586Simp		/*
466141586Simp		 * read in a byte; however we need to always read 16 bits
467141586Simp		 * at a time or the hardware gets into a funny state
468141586Simp		 */
469141586Simp
470141586Simp		if (amount == 1) {
471141586Simp			/* need to read in a short and copy LSB */
472141586Simp			volatile u_short *const s =
473141586Simp				(volatile u_short *) sc->hpp_mem_start;
474141586Simp			*dst = (*s) & 0xFF;
475141586Simp		}
476141586Simp
477141586Simp		/* Restore Boot ROM access.  */
478141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
479141586Simp	} else {
480141586Simp		/* Read in data using the I/O port */
481141586Simp		if (use_32bit_access && (amount > 3)) {
482141586Simp			ed_asic_insl(sc, ED_HPP_PAGE_4, dst, amount >> 2);
483141586Simp			dst += (amount & ~3);
484141586Simp			amount &= 3;
485141586Simp		}
486141586Simp		if (amount > 1) {
487141586Simp			ed_asic_insw(sc, ED_HPP_PAGE_4, dst, amount >> 1);
488141586Simp			dst += (amount & ~1);
489141586Simp			amount &= 1;
490141586Simp		}
491141586Simp		if (amount == 1) { /* read in a short and keep the LSB */
492141586Simp			*dst = ed_asic_inw(sc, ED_HPP_PAGE_4) & 0xFF;
493141586Simp		}
494141586Simp	}
495141586Simp}
496141586Simp
497141586Simp/*
498141586Simp * HP PC Lan+: Write to NIC memory, using either PIO or memory mapped
499141586Simp * IO.
500141586Simp *	Only used in the probe routine to test the memory. 'len' must
501141586Simp *	be even.
502141586Simp */
503141586Simpstatic void
504141586Simped_hpp_writemem(struct ed_softc *sc, uint8_t *src, uint16_t dst, uint16_t len)
505141586Simp{
506141586Simp	/* reset remote DMA complete flag */
507141586Simp	ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
508141586Simp
509141586Simp	/* program the write address in RAM */
510141586Simp	ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
511141586Simp
512141586Simp	if (sc->hpp_mem_start) {
513141586Simp		u_short *s = (u_short *) src;
514141586Simp		volatile u_short *d = (u_short *) sc->hpp_mem_start;
515141586Simp		u_short *const fence = s + (len >> 1);
516141586Simp
517141586Simp		/*
518141586Simp		 * Enable memory mapped access.
519141586Simp		 */
520141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
521141586Simp			~(ED_HPP_OPTION_MEM_DISABLE |
522141586Simp			  ED_HPP_OPTION_BOOT_ROM_ENB));
523141586Simp
524141586Simp		/*
525141586Simp		 * Copy to NIC memory.
526141586Simp		 */
527141586Simp		while (s < fence)
528141586Simp			*d = *s++;
529141586Simp
530141586Simp		/*
531141586Simp		 * Restore Boot ROM access.
532141586Simp		 */
533141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
534141586Simp	} else {
535141586Simp		/* write data using I/O writes */
536141586Simp		ed_asic_outsw(sc, ED_HPP_PAGE_4, src, len / 2);
537141586Simp	}
538141586Simp}
539141586Simp
540141586Simp/*
541141586Simp * Write to HP PC Lan+ NIC memory.  Access to the NIC can be by using
542141586Simp * outsw() or via the memory mapped interface to the same register.
543141586Simp * Writes have to be in word units; byte accesses won't work and may cause
544141586Simp * the NIC to behave weirdly. Long word accesses are permitted if the ASIC
545141586Simp * allows it.
546141586Simp */
547141586Simp
548141586Simpu_short
549141586Simped_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, int dst)
550141586Simp{
551141586Simp	int len, wantbyte;
552141586Simp	unsigned short total_len;
553141586Simp	unsigned char savebyte[2];
554141586Simp	volatile u_short * const d =
555141586Simp		(volatile u_short *) sc->hpp_mem_start;
556141586Simp	int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
557141586Simp
558141586Simp	/* select page 0 registers */
559141586Simp	ed_nic_outb(sc, ED_P0_CR, sc->cr_proto | ED_CR_STA);
560141586Simp
561141586Simp	/* reset remote DMA complete flag */
562141586Simp	ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
563141586Simp
564141586Simp	/* program the write address in RAM */
565141586Simp	ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
566141586Simp
567141586Simp	if (sc->hpp_mem_start) 	/* enable memory mapped I/O */
568141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
569141586Simp			~(ED_HPP_OPTION_MEM_DISABLE |
570141586Simp			ED_HPP_OPTION_BOOT_ROM_ENB));
571141586Simp
572141586Simp	wantbyte = 0;
573141586Simp	total_len = 0;
574141586Simp
575141586Simp	if (sc->hpp_mem_start) {	/* Memory mapped I/O port */
576141586Simp		while (m) {
577141586Simp			total_len += (len = m->m_len);
578141586Simp			if (len) {
579141586Simp				caddr_t data = mtod(m, caddr_t);
580141586Simp				/* finish the last word of the previous mbuf */
581141586Simp				if (wantbyte) {
582141586Simp					savebyte[1] = *data;
583141586Simp					*d = *((u_short *) savebyte);
584141586Simp					data++; len--; wantbyte = 0;
585141586Simp				}
586141586Simp				/* output contiguous words */
587141586Simp				if ((len > 3) && (use_32bit_accesses)) {
588141586Simp					volatile uint32_t *const dl =
589141586Simp						(volatile uint32_t *) d;
590141586Simp					uint32_t *sl = (uint32_t *) data;
591141586Simp					uint32_t *fence = sl + (len >> 2);
592141586Simp
593141586Simp					while (sl < fence)
594141586Simp						*dl = *sl++;
595141586Simp
596141586Simp					data += (len & ~3);
597141586Simp					len &= 3;
598141586Simp				}
599141586Simp				/* finish off remain 16 bit writes */
600141586Simp				if (len > 1) {
601141586Simp					u_short *s = (u_short *) data;
602141586Simp					u_short *fence = s + (len >> 1);
603141586Simp
604141586Simp					while (s < fence)
605141586Simp						*d = *s++;
606141586Simp
607141586Simp					data += (len & ~1);
608141586Simp					len &= 1;
609141586Simp				}
610141586Simp				/* save last byte if needed */
611141586Simp				if ((wantbyte = (len == 1)) != 0)
612141586Simp					savebyte[0] = *data;
613141586Simp			}
614141586Simp			m = m->m_next;	/* to next mbuf */
615141586Simp		}
616141586Simp		if (wantbyte) /* write last byte */
617141586Simp			*d = *((u_short *) savebyte);
618141586Simp	} else {
619141586Simp		/* use programmed I/O */
620141586Simp		while (m) {
621141586Simp			total_len += (len = m->m_len);
622141586Simp			if (len) {
623141586Simp				caddr_t data = mtod(m, caddr_t);
624141586Simp				/* finish the last word of the previous mbuf */
625141586Simp				if (wantbyte) {
626141586Simp					savebyte[1] = *data;
627141586Simp					ed_asic_outw(sc, ED_HPP_PAGE_4,
628141586Simp						     *((u_short *)savebyte));
629141586Simp					data++;
630141586Simp					len--;
631141586Simp					wantbyte = 0;
632141586Simp				}
633141586Simp				/* output contiguous words */
634141586Simp				if ((len > 3) && use_32bit_accesses) {
635141586Simp					ed_asic_outsl(sc, ED_HPP_PAGE_4,
636141586Simp						      data, len >> 2);
637141586Simp					data += (len & ~3);
638141586Simp					len &= 3;
639141586Simp				}
640141586Simp				/* finish off remaining 16 bit accesses */
641141586Simp				if (len > 1) {
642141586Simp					ed_asic_outsw(sc, ED_HPP_PAGE_4,
643141586Simp						      data, len >> 1);
644141586Simp					data += (len & ~1);
645141586Simp					len &= 1;
646141586Simp				}
647141586Simp				if ((wantbyte = (len == 1)) != 0)
648141586Simp					savebyte[0] = *data;
649141586Simp
650141586Simp			} /* if len != 0 */
651141586Simp			m = m->m_next;
652141586Simp		}
653141586Simp		if (wantbyte) /* spit last byte */
654141586Simp			ed_asic_outw(sc, ED_HPP_PAGE_4, *(u_short *)savebyte);
655141586Simp
656141586Simp	}
657141586Simp
658141586Simp	if (sc->hpp_mem_start)	/* turn off memory mapped i/o */
659141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
660141586Simp
661141586Simp	return (total_len);
662141586Simp}
663141586Simp
664141586Simp#endif /* ED_HPP */
665