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