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$");
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>
53257176Sglebius#include <net/if_var.h>		/* XXX: ed_hpp_set_physical_link() */
54141586Simp#include <net/if_arp.h>
55141586Simp#include <net/if_dl.h>
56141586Simp#include <net/if_mib.h>
57141586Simp#include <net/if_media.h>
58141586Simp
59141586Simp#include <net/bpf.h>
60141586Simp
61141586Simp#include <dev/ed/if_edreg.h>
62141586Simp#include <dev/ed/if_edvar.h>
63141586Simp
64154924Simpstatic void	ed_hpp_readmem(struct ed_softc *, bus_size_t, uint8_t *,
65154924Simp		    uint16_t);
66141586Simpstatic void	ed_hpp_writemem(struct ed_softc *, uint8_t *, uint16_t,
67141586Simp		    uint16_t);
68154895Simpstatic void	ed_hpp_set_physical_link(struct ed_softc *sc);
69154924Simpstatic u_short	ed_hpp_write_mbufs(struct ed_softc *, struct mbuf *,
70154924Simp		    bus_size_t);
71141586Simp
72141586Simp/*
73141586Simp * Interrupt conversion table for the HP PC LAN+
74141586Simp */
75141586Simpstatic uint16_t ed_hpp_intr_val[] = {
76141586Simp	0,		/* 0 */
77141586Simp	0,		/* 1 */
78141586Simp	0,		/* 2 */
79141586Simp	3,		/* 3 */
80141586Simp	4,		/* 4 */
81141586Simp	5,		/* 5 */
82141586Simp	6,		/* 6 */
83141586Simp	7,		/* 7 */
84141586Simp	0,		/* 8 */
85141586Simp	9,		/* 9 */
86141586Simp	10,		/* 10 */
87141586Simp	11,		/* 11 */
88141586Simp	12,		/* 12 */
89141586Simp	0,		/* 13 */
90141586Simp	0,		/* 14 */
91141586Simp	15		/* 15 */
92141586Simp};
93141586Simp
94141586Simp#define	ED_HPP_TEST_SIZE	16
95141586Simp
96141586Simp/*
97141586Simp * Probe and vendor specific initialization for the HP PC Lan+ Cards.
98141586Simp * (HP Part nos: 27247B and 27252A).
99141586Simp *
100141586Simp * The card has an asic wrapper around a DS8390 core.  The asic handles
101141586Simp * host accesses and offers both standard register IO and memory mapped
102141586Simp * IO.  Memory mapped I/O allows better performance at the expense of greater
103141586Simp * chance of an incompatibility with existing ISA cards.
104141586Simp *
105141586Simp * The card has a few caveats: it isn't tolerant of byte wide accesses, only
106141586Simp * short (16 bit) or word (32 bit) accesses are allowed.  Some card revisions
107141586Simp * don't allow 32 bit accesses; these are indicated by a bit in the software
108141586Simp * ID register (see if_edreg.h).
109141586Simp *
110141586Simp * Other caveats are: we should read the MAC address only when the card
111141586Simp * is inactive.
112141586Simp *
113141586Simp * For more information; please consult the CRYNWR packet driver.
114141586Simp *
115141586Simp * The AUI port is turned on using the "link2" option on the ifconfig
116141586Simp * command line.
117141586Simp */
118141586Simpint
119141586Simped_probe_HP_pclanp(device_t dev, int port_rid, int flags)
120141586Simp{
121141586Simp	struct ed_softc *sc = device_get_softc(dev);
122141586Simp	int error;
123141586Simp	int n;				/* temp var */
124141586Simp	int memsize;			/* mem on board */
125141586Simp	u_char checksum;		/* checksum of board address */
126141586Simp	u_char irq;			/* board configured IRQ */
127141586Simp	uint8_t test_pattern[ED_HPP_TEST_SIZE];	/* read/write areas for */
128141586Simp	uint8_t test_buffer[ED_HPP_TEST_SIZE];	/* probing card */
129294883Sjhibbits	rman_res_t conf_maddr, conf_msize, conf_irq, junk;
130141586Simp
131141586Simp	error = ed_alloc_port(dev, 0, ED_HPP_IO_PORTS);
132141586Simp	if (error)
133141586Simp		return (error);
134141586Simp
135141586Simp	/* Fill in basic information */
136141586Simp	sc->asic_offset = ED_HPP_ASIC_OFFSET;
137141586Simp	sc->nic_offset  = ED_HPP_NIC_OFFSET;
138141586Simp
139141586Simp	sc->chip_type = ED_CHIP_TYPE_DP8390;
140141586Simp	sc->isa16bit = 0;	/* the 8390 core needs to be in byte mode */
141141586Simp
142141586Simp	/*
143141586Simp	 * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53"
144141586Simp	 */
145141586Simp
146141586Simp	if ((ed_asic_inb(sc, ED_HPP_ID) != 0x50) ||
147141586Simp	    (ed_asic_inb(sc, ED_HPP_ID + 1) != 0x48) ||
148141586Simp	    ((ed_asic_inb(sc, ED_HPP_ID + 2) & 0xF0) != 0) ||
149141586Simp	    (ed_asic_inb(sc, ED_HPP_ID + 3) != 0x53))
150154894Simp		return (ENXIO);
151141586Simp
152141586Simp	/*
153141586Simp	 * Read the MAC address and verify checksum on the address.
154141586Simp	 */
155141586Simp
156141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_MAC);
157141586Simp	for (n  = 0, checksum = 0; n < ETHER_ADDR_LEN; n++)
158147256Sbrooks		checksum += (sc->enaddr[n] =
159141586Simp		    ed_asic_inb(sc, ED_HPP_MAC_ADDR + n));
160141586Simp
161141586Simp	checksum += ed_asic_inb(sc, ED_HPP_MAC_ADDR + ETHER_ADDR_LEN);
162141586Simp
163141586Simp	if (checksum != 0xFF)
164154894Simp		return (ENXIO);
165141586Simp
166141586Simp	/*
167141586Simp	 * Verify that the software model number is 0.
168141586Simp	 */
169141586Simp
170141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_ID);
171141586Simp	if (((sc->hpp_id = ed_asic_inw(sc, ED_HPP_PAGE_4)) &
172141586Simp		ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000)
173154894Simp		return (ENXIO);
174141586Simp
175141586Simp	/*
176141586Simp	 * Read in and save the current options configured on card.
177141586Simp	 */
178141586Simp
179141586Simp	sc->hpp_options = ed_asic_inw(sc, ED_HPP_OPTION);
180141586Simp
181141586Simp	sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET |
182141586Simp	    ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ);
183141586Simp
184141586Simp	/*
185141586Simp	 * Reset the chip.  This requires writing to the option register
186141586Simp	 * so take care to preserve the other bits.
187141586Simp	 */
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
193141586Simp	DELAY(5000);	/* wait for chip reset to complete */
194141586Simp
195141586Simp	ed_asic_outw(sc, ED_HPP_OPTION,
196141586Simp	    (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET |
197141586Simp	    ED_HPP_OPTION_CHIP_RESET |
198141586Simp	    ED_HPP_OPTION_ENABLE_IRQ)));
199141586Simp
200141586Simp	DELAY(5000);
201141586Simp
202141586Simp	if (!(ed_nic_inb(sc, ED_P0_ISR) & ED_ISR_RST))
203154894Simp		return (ENXIO);	/* reset did not complete */
204141586Simp
205141586Simp	/*
206141586Simp	 * Read out configuration information.
207141586Simp	 */
208141586Simp
209141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
210141586Simp
211141586Simp	irq = ed_asic_inb(sc, ED_HPP_HW_IRQ);
212141586Simp
213141586Simp	/*
214141586Simp 	 * Check for impossible IRQ.
215141586Simp	 */
216141586Simp
217298435Spfg	if (irq >= nitems(ed_hpp_intr_val))
218154894Simp		return (ENXIO);
219141586Simp
220141586Simp	/*
221141586Simp	 * If the kernel IRQ was specified with a '?' use the cards idea
222141586Simp	 * of the IRQ.  If the kernel IRQ was explicitly specified, it
223141586Simp 	 * should match that of the hardware.
224141586Simp	 */
225141586Simp	error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk);
226141586Simp	if (error)
227141586Simp		bus_set_resource(dev, SYS_RES_IRQ, 0, ed_hpp_intr_val[irq], 1);
228141586Simp	else {
229141586Simp		if (conf_irq != ed_hpp_intr_val[irq])
230141586Simp			return (ENXIO);
231141586Simp	}
232141586Simp
233141586Simp	/*
234141586Simp	 * Fill in softconfig info.
235141586Simp	 */
236141586Simp
237141586Simp	sc->vendor = ED_VENDOR_HP;
238141586Simp	sc->type = ED_TYPE_HP_PCLANPLUS;
239141586Simp	sc->type_str = "HP-PCLAN+";
240141586Simp
241141586Simp	sc->mem_shared = 0;	/* we DON'T have dual ported RAM */
242141586Simp	sc->mem_start = 0;	/* we use offsets inside the card RAM */
243141586Simp
244141586Simp	sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */
245141586Simp
246141586Simp	/*
247141586Simp	 * The board has 32KB of memory.  Is there a way to determine
248141586Simp	 * this programmatically?
249141586Simp	 */
250141586Simp
251141586Simp	memsize = 32768;
252141586Simp
253141586Simp	/*
254141586Simp	 * Check if memory mapping of the I/O registers possible.
255141586Simp	 */
256141586Simp	if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) {
257141586Simp		u_long mem_addr;
258141586Simp
259141586Simp		/*
260141586Simp		 * determine the memory address from the board.
261141586Simp		 */
262141586Simp
263141586Simp		ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
264141586Simp		mem_addr = (ed_asic_inw(sc, ED_HPP_HW_MEM_MAP) << 8);
265141586Simp
266141586Simp		/*
267141586Simp		 * Check that the kernel specified start of memory and
268141586Simp		 * hardware's idea of it match.
269141586Simp		 */
270141586Simp		error = bus_get_resource(dev, SYS_RES_MEMORY, 0,
271141586Simp					 &conf_maddr, &conf_msize);
272141586Simp		if (error)
273141586Simp			return (error);
274141586Simp
275141586Simp		if (mem_addr != conf_maddr)
276154894Simp			return (ENXIO);
277141586Simp
278141586Simp		error = ed_alloc_memory(dev, 0, memsize);
279141586Simp		if (error)
280141586Simp			return (error);
281141586Simp
282141586Simp		sc->hpp_mem_start = rman_get_virtual(sc->mem_res);
283141586Simp	}
284141586Simp
285141586Simp	/*
286141586Simp	 * Fill in the rest of the soft config structure.
287141586Simp	 */
288141586Simp
289141586Simp	/*
290141586Simp	 * The transmit page index.
291141586Simp	 */
292141586Simp
293141586Simp	sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET;
294141586Simp
295141586Simp	if (device_get_flags(dev) & ED_FLAGS_NO_MULTI_BUFFERING)
296141586Simp		sc->txb_cnt = 1;
297141586Simp	else
298141586Simp		sc->txb_cnt = 2;
299141586Simp
300141586Simp	/*
301141586Simp	 * Memory description
302141586Simp	 */
303141586Simp
304141586Simp	sc->mem_size = memsize;
305141586Simp	sc->mem_ring = sc->mem_start +
306141586Simp		(sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE);
307141586Simp	sc->mem_end = sc->mem_start + sc->mem_size;
308141586Simp
309141586Simp	/*
310141586Simp	 * Receive area starts after the transmit area and
311141586Simp	 * continues till the end of memory.
312141586Simp	 */
313141586Simp
314141586Simp	sc->rec_page_start = sc->tx_page_start +
315141586Simp				(sc->txb_cnt * ED_TXBUF_SIZE);
316141586Simp	sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE);
317141586Simp
318141586Simp
319141586Simp	sc->cr_proto = 0;	/* value works */
320141586Simp
321141586Simp	/*
322141586Simp	 * Set the wrap registers for string I/O reads.
323141586Simp	 */
324141586Simp
325141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW);
326141586Simp	ed_asic_outw(sc, ED_HPP_HW_WRAP,
327141586Simp	    ((sc->rec_page_start / ED_PAGE_SIZE) |
328141586Simp	    (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8)));
329141586Simp
330141586Simp	/*
331141586Simp	 * Reset the register page to normal operation.
332141586Simp	 */
333141586Simp
334141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
335141586Simp
336141586Simp	/*
337141586Simp	 * Verify that we can read/write from adapter memory.
338141586Simp	 * Create test pattern.
339141586Simp	 */
340141586Simp
341141586Simp	for (n = 0; n < ED_HPP_TEST_SIZE; n++)
342141586Simp		test_pattern[n] = (n*n) ^ ~n;
343141586Simp
344141586Simp#undef	ED_HPP_TEST_SIZE
345141586Simp
346141586Simp	/*
347141586Simp	 * Check that the memory is accessible thru the I/O ports.
348141586Simp	 * Write out the contents of "test_pattern", read back
349141586Simp	 * into "test_buffer" and compare the two for any
350141586Simp	 * mismatch.
351141586Simp	 */
352141586Simp
353141586Simp	for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) {
354141586Simp		ed_hpp_writemem(sc, test_pattern, (n * ED_PAGE_SIZE),
355141586Simp				sizeof(test_pattern));
356141586Simp		ed_hpp_readmem(sc, (n * ED_PAGE_SIZE),
357141586Simp			test_buffer, sizeof(test_pattern));
358141586Simp
359141586Simp		if (bcmp(test_pattern, test_buffer,
360141586Simp			sizeof(test_pattern)))
361154894Simp			return (ENXIO);
362141586Simp	}
363141586Simp
364154895Simp	sc->sc_mediachg = ed_hpp_set_physical_link;
365154924Simp	sc->sc_write_mbufs = ed_hpp_write_mbufs;
366154924Simp	sc->readmem = ed_hpp_readmem;
367141586Simp	return (0);
368141586Simp}
369141586Simp
370141586Simp/*
371141586Simp * HP PC Lan+ : Set the physical link to use AUI or TP/TL.
372141586Simp */
373141586Simp
374154895Simpstatic void
375141586Simped_hpp_set_physical_link(struct ed_softc *sc)
376141586Simp{
377147256Sbrooks	struct ifnet *ifp = sc->ifp;
378141586Simp	int lan_page;
379141586Simp
380141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
381141586Simp	lan_page = ed_asic_inw(sc, ED_HPP_PAGE_0);
382141586Simp
383154892Simp	if (ifp->if_flags & IFF_LINK2) {
384141586Simp		/*
385141586Simp		 * Use the AUI port.
386141586Simp		 */
387141586Simp
388141586Simp		lan_page |= ED_HPP_LAN_AUI;
389141586Simp		ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
390141586Simp		ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
391141586Simp	} else {
392141586Simp		/*
393141586Simp		 * Use the ThinLan interface
394141586Simp		 */
395141586Simp
396141586Simp		lan_page &= ~ED_HPP_LAN_AUI;
397141586Simp		ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN);
398141586Simp		ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page);
399141586Simp	}
400141586Simp
401141586Simp	/*
402141586Simp	 * Wait for the lan card to re-initialize itself
403141586Simp	 */
404141586Simp	DELAY(150000);	/* wait 150 ms */
405141586Simp
406141586Simp	/*
407141586Simp	 * Restore normal pages.
408141586Simp	 */
409141586Simp	ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF);
410141586Simp}
411141586Simp
412141586Simp/*
413141586Simp * Support routines to handle the HP PC Lan+ card.
414141586Simp */
415141586Simp
416141586Simp/*
417141586Simp * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped
418141586Simp * IO.
419141586Simp */
420141586Simp
421154924Simpstatic void
422149558Simped_hpp_readmem(struct ed_softc *sc, bus_size_t src, uint8_t *dst,
423149558Simp    uint16_t amount)
424141586Simp{
425141586Simp	int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
426141586Simp
427141586Simp	/* Program the source address in RAM */
428141586Simp	ed_asic_outw(sc, ED_HPP_PAGE_2, src);
429141586Simp
430141586Simp	/*
431141586Simp	 * The HP PC Lan+ card supports word reads as well as
432141586Simp	 * a memory mapped i/o port that is aliased to every
433141586Simp	 * even address on the board.
434141586Simp	 */
435141586Simp	if (sc->hpp_mem_start) {
436141586Simp		/* Enable memory mapped access.  */
437141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
438141586Simp			~(ED_HPP_OPTION_MEM_DISABLE |
439141586Simp			  ED_HPP_OPTION_BOOT_ROM_ENB));
440141586Simp
441141586Simp		if (use_32bit_access && (amount > 3)) {
442141586Simp			uint32_t *dl = (uint32_t *) dst;
443141586Simp			volatile uint32_t *const sl =
444141586Simp				(uint32_t *) sc->hpp_mem_start;
445141586Simp			uint32_t *const fence = dl + (amount >> 2);
446141586Simp
447149558Simp			/*
448149558Simp			 * Copy out NIC data.  We could probably write this
449149558Simp			 * as a `movsl'. The currently generated code is lousy.
450149558Simp			 */
451141586Simp			while (dl < fence)
452141586Simp				*dl++ = *sl;
453141586Simp
454141586Simp			dst += (amount & ~3);
455141586Simp			amount &= 3;
456141586Simp
457141586Simp		}
458141586Simp
459141586Simp		/* Finish off any words left, as a series of short reads */
460141586Simp		if (amount > 1) {
461141586Simp			u_short *d = (u_short *) dst;
462141586Simp			volatile u_short *const s =
463141586Simp				(u_short *) sc->hpp_mem_start;
464141586Simp			u_short *const fence = d + (amount >> 1);
465141586Simp
466141586Simp			/* Copy out NIC data.  */
467141586Simp			while (d < fence)
468141586Simp				*d++ = *s;
469141586Simp
470141586Simp			dst += (amount & ~1);
471141586Simp			amount &= 1;
472141586Simp		}
473141586Simp
474141586Simp		/*
475141586Simp		 * read in a byte; however we need to always read 16 bits
476141586Simp		 * at a time or the hardware gets into a funny state
477141586Simp		 */
478141586Simp
479141586Simp		if (amount == 1) {
480141586Simp			/* need to read in a short and copy LSB */
481141586Simp			volatile u_short *const s =
482141586Simp				(volatile u_short *) sc->hpp_mem_start;
483141586Simp			*dst = (*s) & 0xFF;
484141586Simp		}
485141586Simp
486141586Simp		/* Restore Boot ROM access.  */
487141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
488141586Simp	} else {
489141586Simp		/* Read in data using the I/O port */
490141586Simp		if (use_32bit_access && (amount > 3)) {
491141586Simp			ed_asic_insl(sc, ED_HPP_PAGE_4, dst, amount >> 2);
492141586Simp			dst += (amount & ~3);
493141586Simp			amount &= 3;
494141586Simp		}
495141586Simp		if (amount > 1) {
496141586Simp			ed_asic_insw(sc, ED_HPP_PAGE_4, dst, amount >> 1);
497141586Simp			dst += (amount & ~1);
498141586Simp			amount &= 1;
499141586Simp		}
500141586Simp		if (amount == 1) { /* read in a short and keep the LSB */
501141586Simp			*dst = ed_asic_inw(sc, ED_HPP_PAGE_4) & 0xFF;
502141586Simp		}
503141586Simp	}
504141586Simp}
505141586Simp
506141586Simp/*
507141586Simp * HP PC Lan+: Write to NIC memory, using either PIO or memory mapped
508141586Simp * IO.
509141586Simp *	Only used in the probe routine to test the memory. 'len' must
510141586Simp *	be even.
511141586Simp */
512141586Simpstatic void
513141586Simped_hpp_writemem(struct ed_softc *sc, uint8_t *src, uint16_t dst, uint16_t len)
514141586Simp{
515141586Simp	/* reset remote DMA complete flag */
516141586Simp	ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
517141586Simp
518141586Simp	/* program the write address in RAM */
519141586Simp	ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
520141586Simp
521141586Simp	if (sc->hpp_mem_start) {
522141586Simp		u_short *s = (u_short *) src;
523141586Simp		volatile u_short *d = (u_short *) sc->hpp_mem_start;
524141586Simp		u_short *const fence = s + (len >> 1);
525141586Simp
526141586Simp		/*
527141586Simp		 * Enable memory mapped access.
528141586Simp		 */
529141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
530141586Simp			~(ED_HPP_OPTION_MEM_DISABLE |
531141586Simp			  ED_HPP_OPTION_BOOT_ROM_ENB));
532141586Simp
533141586Simp		/*
534141586Simp		 * Copy to NIC memory.
535141586Simp		 */
536141586Simp		while (s < fence)
537141586Simp			*d = *s++;
538141586Simp
539141586Simp		/*
540141586Simp		 * Restore Boot ROM access.
541141586Simp		 */
542141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
543141586Simp	} else {
544141586Simp		/* write data using I/O writes */
545141586Simp		ed_asic_outsw(sc, ED_HPP_PAGE_4, src, len / 2);
546141586Simp	}
547141586Simp}
548141586Simp
549141586Simp/*
550141586Simp * Write to HP PC Lan+ NIC memory.  Access to the NIC can be by using
551141586Simp * outsw() or via the memory mapped interface to the same register.
552141586Simp * Writes have to be in word units; byte accesses won't work and may cause
553141586Simp * the NIC to behave weirdly. Long word accesses are permitted if the ASIC
554141586Simp * allows it.
555141586Simp */
556141586Simp
557154924Simpstatic u_short
558154924Simped_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, bus_size_t dst)
559141586Simp{
560141586Simp	int len, wantbyte;
561141586Simp	unsigned short total_len;
562141586Simp	unsigned char savebyte[2];
563141586Simp	volatile u_short * const d =
564141586Simp		(volatile u_short *) sc->hpp_mem_start;
565141586Simp	int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS);
566141586Simp
567141586Simp	/* select page 0 registers */
568261528Smarius	ed_nic_barrier(sc, ED_P0_CR, 1,
569261528Smarius	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
570141586Simp	ed_nic_outb(sc, ED_P0_CR, sc->cr_proto | ED_CR_STA);
571261528Smarius	ed_nic_barrier(sc, ED_P0_CR, 1,
572261528Smarius	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
573141586Simp
574141586Simp	/* reset remote DMA complete flag */
575141586Simp	ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC);
576141586Simp
577141586Simp	/* program the write address in RAM */
578141586Simp	ed_asic_outw(sc, ED_HPP_PAGE_0, dst);
579141586Simp
580141586Simp	if (sc->hpp_mem_start) 	/* enable memory mapped I/O */
581141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options &
582141586Simp			~(ED_HPP_OPTION_MEM_DISABLE |
583141586Simp			ED_HPP_OPTION_BOOT_ROM_ENB));
584141586Simp
585141586Simp	wantbyte = 0;
586141586Simp	total_len = 0;
587141586Simp
588141586Simp	if (sc->hpp_mem_start) {	/* Memory mapped I/O port */
589141586Simp		while (m) {
590141586Simp			total_len += (len = m->m_len);
591141586Simp			if (len) {
592141586Simp				caddr_t data = mtod(m, caddr_t);
593141586Simp				/* finish the last word of the previous mbuf */
594141586Simp				if (wantbyte) {
595141586Simp					savebyte[1] = *data;
596141586Simp					*d = *((u_short *) savebyte);
597141586Simp					data++; len--; wantbyte = 0;
598141586Simp				}
599141586Simp				/* output contiguous words */
600141586Simp				if ((len > 3) && (use_32bit_accesses)) {
601141586Simp					volatile uint32_t *const dl =
602141586Simp						(volatile uint32_t *) d;
603141586Simp					uint32_t *sl = (uint32_t *) data;
604141586Simp					uint32_t *fence = sl + (len >> 2);
605141586Simp
606141586Simp					while (sl < fence)
607141586Simp						*dl = *sl++;
608141586Simp
609141586Simp					data += (len & ~3);
610141586Simp					len &= 3;
611141586Simp				}
612141586Simp				/* finish off remain 16 bit writes */
613141586Simp				if (len > 1) {
614141586Simp					u_short *s = (u_short *) data;
615141586Simp					u_short *fence = s + (len >> 1);
616141586Simp
617141586Simp					while (s < fence)
618141586Simp						*d = *s++;
619141586Simp
620141586Simp					data += (len & ~1);
621141586Simp					len &= 1;
622141586Simp				}
623141586Simp				/* save last byte if needed */
624141586Simp				if ((wantbyte = (len == 1)) != 0)
625141586Simp					savebyte[0] = *data;
626141586Simp			}
627141586Simp			m = m->m_next;	/* to next mbuf */
628141586Simp		}
629141586Simp		if (wantbyte) /* write last byte */
630141586Simp			*d = *((u_short *) savebyte);
631141586Simp	} else {
632141586Simp		/* use programmed I/O */
633141586Simp		while (m) {
634141586Simp			total_len += (len = m->m_len);
635141586Simp			if (len) {
636141586Simp				caddr_t data = mtod(m, caddr_t);
637141586Simp				/* finish the last word of the previous mbuf */
638141586Simp				if (wantbyte) {
639141586Simp					savebyte[1] = *data;
640141586Simp					ed_asic_outw(sc, ED_HPP_PAGE_4,
641141586Simp						     *((u_short *)savebyte));
642141586Simp					data++;
643141586Simp					len--;
644141586Simp					wantbyte = 0;
645141586Simp				}
646141586Simp				/* output contiguous words */
647141586Simp				if ((len > 3) && use_32bit_accesses) {
648141586Simp					ed_asic_outsl(sc, ED_HPP_PAGE_4,
649141586Simp						      data, len >> 2);
650141586Simp					data += (len & ~3);
651141586Simp					len &= 3;
652141586Simp				}
653141586Simp				/* finish off remaining 16 bit accesses */
654141586Simp				if (len > 1) {
655141586Simp					ed_asic_outsw(sc, ED_HPP_PAGE_4,
656141586Simp						      data, len >> 1);
657141586Simp					data += (len & ~1);
658141586Simp					len &= 1;
659141586Simp				}
660141586Simp				if ((wantbyte = (len == 1)) != 0)
661141586Simp					savebyte[0] = *data;
662141586Simp
663141586Simp			} /* if len != 0 */
664141586Simp			m = m->m_next;
665141586Simp		}
666141586Simp		if (wantbyte) /* spit last byte */
667141586Simp			ed_asic_outw(sc, ED_HPP_PAGE_4, *(u_short *)savebyte);
668141586Simp
669141586Simp	}
670141586Simp
671141586Simp	if (sc->hpp_mem_start)	/* turn off memory mapped i/o */
672141586Simp		ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options);
673141586Simp
674141586Simp	return (total_len);
675141586Simp}
676141586Simp
677141586Simp#endif /* ED_HPP */
678