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