if_ed_wd80x3.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_wd80x3.c 141586 2005-02-09 20:03:40Z imp $");
32
33#include "opt_ed.h"
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/sockio.h>
38#include <sys/mbuf.h>
39#include <sys/kernel.h>
40#include <sys/socket.h>
41#include <sys/syslog.h>
42
43#include <sys/bus.h>
44
45#include <machine/bus.h>
46#include <sys/rman.h>
47#include <machine/resource.h>
48
49#include <net/ethernet.h>
50#include <net/if.h>
51#include <net/if_arp.h>
52#include <net/if_dl.h>
53#include <net/if_mib.h>
54#include <net/if_media.h>
55
56#include <net/bpf.h>
57
58#include <dev/ed/if_edreg.h>
59#include <dev/ed/if_edvar.h>
60
61/*
62 * Interrupt conversion table for WD/SMC ASIC/83C584
63 */
64static uint16_t ed_intr_val[] = {
65	9,
66	3,
67	5,
68	7,
69	10,
70	11,
71	15,
72	4
73};
74
75/*
76 * Interrupt conversion table for 83C790
77 */
78static uint16_t ed_790_intr_val[] = {
79	0,
80	9,
81	3,
82	5,
83	7,
84	10,
85	11,
86	15
87};
88
89static void
90ed_disable_16bit_access(struct ed_softc *sc)
91{
92	/*
93	 * Disable 16 bit access to shared memory
94	 */
95	if (sc->isa16bit) {
96		if (sc->chip_type == ED_CHIP_TYPE_WD790)
97			ed_asic_outb(sc, ED_WD_MSR, 0x00);
98		ed_asic_outb(sc, ED_WD_LAAR, sc->wd_laar_proto &
99		    ~ED_WD_LAAR_M16EN);
100	}
101}
102
103/*
104 * Probe and vendor-specific initialization routine for SMC/WD80x3 boards
105 */
106int
107ed_probe_WD80x3_generic(device_t dev, int flags, uint16_t *intr_vals[])
108{
109	struct ed_softc *sc = device_get_softc(dev);
110	int	error;
111	int     i;
112	u_int   memsize;
113	u_char  iptr, isa16bit, sum, totalsum;
114	u_long	irq, junk, pmem;
115
116	sc->chip_type = ED_CHIP_TYPE_DP8390;
117
118	if (ED_FLAGS_GETTYPE(flags) == ED_FLAGS_TOSH_ETHER) {
119		totalsum = ED_WD_ROM_CHECKSUM_TOTAL_TOSH_ETHER;
120		ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_POW);
121		DELAY(10000);
122	}
123	else
124		totalsum = ED_WD_ROM_CHECKSUM_TOTAL;
125
126	/*
127	 * Attempt to do a checksum over the station address PROM. If it
128	 * fails, it's probably not a SMC/WD board. There is a problem with
129	 * this, though: some clone WD boards don't pass the checksum test.
130	 * Danpex boards for one.
131	 */
132	for (sum = 0, i = 0; i < 8; ++i)
133		sum += ed_asic_inb(sc, ED_WD_PROM + i);
134
135	if (sum != totalsum) {
136
137		/*
138		 * Checksum is invalid. This often happens with cheap WD8003E
139		 * clones.  In this case, the checksum byte (the eighth byte)
140		 * seems to always be zero.
141		 */
142		if (ed_asic_inb(sc, ED_WD_CARD_ID) != ED_TYPE_WD8003E ||
143		    ed_asic_inb(sc, ED_WD_PROM + 7) != 0)
144			return (ENXIO);
145	}
146	/* reset card to force it into a known state. */
147	if (ED_FLAGS_GETTYPE(flags) == ED_FLAGS_TOSH_ETHER)
148		ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW);
149	else
150		ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_RST);
151
152	DELAY(100);
153	ed_asic_outb(sc, ED_WD_MSR, ed_asic_inb(sc, ED_WD_MSR) & ~ED_WD_MSR_RST);
154	/* wait in the case this card is reading its EEROM */
155	DELAY(5000);
156
157	sc->vendor = ED_VENDOR_WD_SMC;
158	sc->type = ed_asic_inb(sc, ED_WD_CARD_ID);
159
160	/*
161	 * Set initial values for width/size.
162	 */
163	memsize = 8192;
164	isa16bit = 0;
165	switch (sc->type) {
166	case ED_TYPE_WD8003S:
167		sc->type_str = "WD8003S";
168		break;
169	case ED_TYPE_WD8003E:
170		sc->type_str = "WD8003E";
171		break;
172	case ED_TYPE_WD8003EB:
173		sc->type_str = "WD8003EB";
174		break;
175	case ED_TYPE_WD8003W:
176		sc->type_str = "WD8003W";
177		break;
178	case ED_TYPE_WD8013EBT:
179		sc->type_str = "WD8013EBT";
180		memsize = 16384;
181		isa16bit = 1;
182		break;
183	case ED_TYPE_WD8013W:
184		sc->type_str = "WD8013W";
185		memsize = 16384;
186		isa16bit = 1;
187		break;
188	case ED_TYPE_WD8013EP:	/* also WD8003EP */
189		if (ed_asic_inb(sc, ED_WD_ICR) & ED_WD_ICR_16BIT) {
190			isa16bit = 1;
191			memsize = 16384;
192			sc->type_str = "WD8013EP";
193		} else
194			sc->type_str = "WD8003EP";
195		break;
196	case ED_TYPE_WD8013WC:
197		sc->type_str = "WD8013WC";
198		memsize = 16384;
199		isa16bit = 1;
200		break;
201	case ED_TYPE_WD8013EBP:
202		sc->type_str = "WD8013EBP";
203		memsize = 16384;
204		isa16bit = 1;
205		break;
206	case ED_TYPE_WD8013EPC:
207		sc->type_str = "WD8013EPC";
208		memsize = 16384;
209		isa16bit = 1;
210		break;
211	case ED_TYPE_SMC8216C: /* 8216 has 16K shared mem -- 8416 has 8K */
212	case ED_TYPE_SMC8216T:
213		if (sc->type == ED_TYPE_SMC8216C)
214			sc->type_str = "SMC8216/SMC8216C";
215		else
216			sc->type_str = "SMC8216T";
217
218		ed_asic_outb(sc, ED_WD790_HWR,
219		    ed_asic_inb(sc, ED_WD790_HWR) | ED_WD790_HWR_SWH);
220		switch (ed_asic_inb(sc, ED_WD790_RAR) & ED_WD790_RAR_SZ64) {
221		case ED_WD790_RAR_SZ64:
222			memsize = 65536;
223			break;
224		case ED_WD790_RAR_SZ32:
225			memsize = 32768;
226			break;
227		case ED_WD790_RAR_SZ16:
228			memsize = 16384;
229			break;
230		case ED_WD790_RAR_SZ8:
231			/* 8216 has 16K shared mem -- 8416 has 8K */
232			if (sc->type == ED_TYPE_SMC8216C)
233				sc->type_str = "SMC8416C/SMC8416BT";
234			else
235				sc->type_str = "SMC8416T";
236			memsize = 8192;
237			break;
238		}
239		ed_asic_outb(sc, ED_WD790_HWR,
240		    ed_asic_inb(sc, ED_WD790_HWR) & ~ED_WD790_HWR_SWH);
241
242		isa16bit = 1;
243		sc->chip_type = ED_CHIP_TYPE_WD790;
244		break;
245	case ED_TYPE_TOSHIBA1:
246		sc->type_str = "Toshiba1";
247		memsize = 32768;
248		isa16bit = 1;
249		break;
250	case ED_TYPE_TOSHIBA4:
251		sc->type_str = "Toshiba4";
252		memsize = 32768;
253		isa16bit = 1;
254		break;
255	default:
256		sc->type_str = "";
257		break;
258	}
259
260	/*
261	 * Make some adjustments to initial values depending on what is found
262	 * in the ICR.
263	 */
264	if (isa16bit && (sc->type != ED_TYPE_WD8013EBT)
265	  && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4)
266	    && ((ed_asic_inb(sc, ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) {
267		isa16bit = 0;
268		memsize = 8192;
269	}
270
271	/* Override memsize? XXX */
272	error = ed_alloc_memory(dev, 0, memsize);
273	if (error)
274		return (error);
275	sc->mem_start = (caddr_t) rman_get_virtual(sc->mem_res);
276
277#ifdef ED_DEBUG
278	printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%lu\n",
279	    sc->type, sc->type_str, isa16bit, memsize,
280	    rman_get_size(sc->mem_res));
281	for (i = 0; i < 8; i++)
282		printf("%x -> %x\n", i, ed_asic_inb(sc, i));
283#endif
284	pmem = rman_get_start(sc->mem_res);
285	error = ed_isa_mem_ok(dev, pmem, memsize);
286	if (error)
287		return (error);
288
289	/*
290	 * (note that if the user specifies both of the following flags that
291	 * '8bit' mode intentionally has precedence)
292	 */
293	if (flags & ED_FLAGS_FORCE_16BIT_MODE)
294		isa16bit = 1;
295	if (flags & ED_FLAGS_FORCE_8BIT_MODE)
296		isa16bit = 0;
297
298	/*
299	 * If possible, get the assigned interrupt number from the card and
300	 * use it.
301	 */
302	if ((sc->type & ED_WD_SOFTCONFIG) &&
303	    (sc->chip_type != ED_CHIP_TYPE_WD790)) {
304
305		/*
306		 * Assemble together the encoded interrupt number.
307		 */
308		iptr = (ed_asic_inb(sc, ED_WD_ICR) & ED_WD_ICR_IR2) |
309		    ((ed_asic_inb(sc, ED_WD_IRR) &
310		      (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5);
311
312		/*
313		 * If no interrupt specified (or "?"), use what the board tells us.
314		 */
315		error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, &junk);
316		if (error && intr_vals[0] != NULL)
317			error = bus_set_resource(dev, SYS_RES_IRQ, 0,
318			    intr_vals[0][iptr], 1);
319		if (error)
320			return (error);
321
322		/*
323		 * Enable the interrupt.
324		 */
325		ed_asic_outb(sc, ED_WD_IRR,
326		     ed_asic_inb(sc, ED_WD_IRR) | ED_WD_IRR_IEN);
327	}
328	if (sc->chip_type == ED_CHIP_TYPE_WD790) {
329		ed_asic_outb(sc, ED_WD790_HWR,
330		  ed_asic_inb(sc, ED_WD790_HWR) | ED_WD790_HWR_SWH);
331		iptr = (((ed_asic_inb(sc, ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) |
332			(ed_asic_inb(sc, ED_WD790_GCR) &
333			 (ED_WD790_GCR_IR1 | ED_WD790_GCR_IR0)) >> 2);
334		ed_asic_outb(sc, ED_WD790_HWR,
335		    ed_asic_inb(sc, ED_WD790_HWR) & ~ED_WD790_HWR_SWH);
336
337		/*
338		 * If no interrupt specified (or "?"), use what the board tells us.
339		 */
340		error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, &junk);
341		if (error && intr_vals[1] != NULL)
342			error = bus_set_resource(dev, SYS_RES_IRQ, 0,
343			  intr_vals[1][iptr], 1);
344		if (error)
345			return (error);
346
347		/*
348		 * Enable interrupts.
349		 */
350		ed_asic_outb(sc, ED_WD790_ICR,
351		  ed_asic_inb(sc, ED_WD790_ICR) | ED_WD790_ICR_EIL);
352	}
353	error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, &junk);
354	if (error) {
355		device_printf(dev, "%s cards don't support auto-detected/assigned interrupts.\n",
356			      sc->type_str);
357		return (ENXIO);
358	}
359	sc->isa16bit = isa16bit;
360	sc->mem_shared = 1;
361
362	/*
363	 * allocate one xmit buffer if < 16k, two buffers otherwise
364	 */
365	if (memsize < 16384 || (flags & ED_FLAGS_NO_MULTI_BUFFERING))
366		sc->txb_cnt = 1;
367	else
368		sc->txb_cnt = 2;
369	sc->tx_page_start = ED_WD_PAGE_OFFSET;
370	sc->rec_page_start = ED_WD_PAGE_OFFSET + ED_TXBUF_SIZE * sc->txb_cnt;
371	sc->rec_page_stop = ED_WD_PAGE_OFFSET + memsize / ED_PAGE_SIZE;
372	sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * sc->rec_page_start);
373	sc->mem_size = memsize;
374	sc->mem_end = sc->mem_start + memsize;
375
376	/*
377	 * Get station address from on-board ROM
378	 */
379	for (i = 0; i < ETHER_ADDR_LEN; ++i)
380		sc->arpcom.ac_enaddr[i] = ed_asic_inb(sc, ED_WD_PROM + i);
381
382	/*
383	 * Set upper address bits and 8/16 bit access to shared memory.
384	 */
385	if (isa16bit) {
386		if (sc->chip_type == ED_CHIP_TYPE_WD790)
387			sc->wd_laar_proto = ed_asic_inb(sc, ED_WD_LAAR);
388		else
389			sc->wd_laar_proto = ED_WD_LAAR_L16EN |
390			    ((pmem >> 19) & ED_WD_LAAR_ADDRHI);
391		/*
392		 * Enable 16bit access
393		 */
394		ed_asic_outb(sc, ED_WD_LAAR, sc->wd_laar_proto |
395		    ED_WD_LAAR_M16EN);
396	} else {
397		if (((sc->type & ED_WD_SOFTCONFIG) ||
398		     (sc->type == ED_TYPE_TOSHIBA1) ||
399		     (sc->type == ED_TYPE_TOSHIBA4) ||
400		     (sc->type == ED_TYPE_WD8013EBT)) &&
401		    (sc->chip_type != ED_CHIP_TYPE_WD790)) {
402			sc->wd_laar_proto = (pmem >> 19) &
403			    ED_WD_LAAR_ADDRHI;
404			ed_asic_outb(sc, ED_WD_LAAR, sc->wd_laar_proto);
405		}
406	}
407
408	/*
409	 * Set address and enable interface shared memory.
410	 */
411	if (sc->chip_type != ED_CHIP_TYPE_WD790) {
412		if (ED_FLAGS_GETTYPE(flags) == ED_FLAGS_TOSH_ETHER) {
413			ed_asic_outb(sc, ED_WD_MSR + 1,
414			    ((pmem >> 8) & 0xe0) | 4);
415			ed_asic_outb(sc, ED_WD_MSR + 2, ((pmem >> 16) & 0x0f));
416			ed_asic_outb(sc, ED_WD_MSR,
417			    ED_WD_MSR_MENB | ED_WD_MSR_POW);
418		} else {
419			ed_asic_outb(sc, ED_WD_MSR, ((pmem >> 13) &
420			    ED_WD_MSR_ADDR) | ED_WD_MSR_MENB);
421		}
422		sc->cr_proto = ED_CR_RD2;
423	} else {
424		ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_MENB);
425		ed_asic_outb(sc, ED_WD790_HWR,
426		    (ed_asic_inb(sc, ED_WD790_HWR) | ED_WD790_HWR_SWH));
427		ed_asic_outb(sc, ED_WD790_RAR,
428		    ((pmem >> 13) & 0x0f) | ((pmem >> 11) & 0x40) |
429		     (ed_asic_inb(sc, ED_WD790_RAR) & 0xb0));
430		ed_asic_outb(sc, ED_WD790_HWR,
431		    (ed_asic_inb(sc, ED_WD790_HWR) & ~ED_WD790_HWR_SWH));
432		sc->cr_proto = 0;
433	}
434
435	error = ed_clear_memory(dev);
436	if (error) {
437		ed_disable_16bit_access(sc);
438		return (error);
439	}
440
441	/*
442	 * Disable 16bit access to shared memory - we leave it
443	 * disabled so that 1) machines reboot properly when the board
444	 * is set 16 bit mode and there are conflicting 8bit
445	 * devices/ROMS in the same 128k address space as this boards
446	 * shared memory. and 2) so that other 8 bit devices with
447	 * shared memory can be used in this 128k region, too.
448	 */
449	ed_disable_16bit_access(sc);
450	return (0);
451}
452
453int
454ed_probe_WD80x3(device_t dev, int port_rid, int flags)
455{
456	struct ed_softc *sc = device_get_softc(dev);
457	int	error;
458	static uint16_t *intr_vals[] = {ed_intr_val, ed_790_intr_val};
459
460	error = ed_alloc_port(dev, port_rid, ED_WD_IO_PORTS);
461	if (error)
462		return (error);
463
464	sc->asic_offset = ED_WD_ASIC_OFFSET;
465	sc->nic_offset  = ED_WD_NIC_OFFSET;
466
467	return ed_probe_WD80x3_generic(dev, flags, intr_vals);
468}
469