aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/ed/if_ed_wd80x3.c
diff options
context:
space:
mode:
authorWarner Losh <imp@FreeBSD.org>2005-02-09 20:03:40 +0000
committerWarner Losh <imp@FreeBSD.org>2005-02-09 20:03:40 +0000
commit969eaf2179e64707a7c6b606b7c7717cd5d734ed (patch)
treee520b94cdc40902296c7025be4da781935e62252 /sys/dev/ed/if_ed_wd80x3.c
parent73f9c36206611415668455657b68e34c5d172543 (diff)
downloadsrc-969eaf2179e64707a7c6b606b7c7717cd5d734ed.tar.gz
src-969eaf2179e64707a7c6b606b7c7717cd5d734ed.zip
Break out obscure ISA cards into their own files, as well as ne2000
and wd80x3 support. Make the obscure ISA cards optional, and add those options to NOTES on i386 (note: the ifdef around the whole code is for module building). Tweak pc98 ed support to include wd80x3 too. Add goo for alpha too. The affected cards are the 3Com 3C503, HP LAN+ and SIC (whatever that is). I couldn't find any of these for sale on ebay, so they are untested. If you have one of these cards, and send it to me, I'll ensure that you have no future problems with it... Minor cleanups as well by using functions rather than cut and paste code for some probing operations (where the function call overhead is lost in the noise). Remove use of kvtop, since they aren't required anymore. This driver needs to get its memory mapped act together, however, and use bus space. It doesn't right now. This reduces the size of if_ed.ko from about 51k to 33k on my laptop.
Notes
Notes: svn path=/head/; revision=141586
Diffstat (limited to 'sys/dev/ed/if_ed_wd80x3.c')
-rw-r--r--sys/dev/ed/if_ed_wd80x3.c468
1 files changed, 468 insertions, 0 deletions
diff --git a/sys/dev/ed/if_ed_wd80x3.c b/sys/dev/ed/if_ed_wd80x3.c
new file mode 100644
index 000000000000..2ecbbb08bbe8
--- /dev/null
+++ b/sys/dev/ed/if_ed_wd80x3.c
@@ -0,0 +1,468 @@
+/*-
+ * Copyright (c) 2005, M. Warner Losh
+ * All rights reserved.
+ * Copyright (c) 1995, David Greenman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ed.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_mib.h>
+#include <net/if_media.h>
+
+#include <net/bpf.h>
+
+#include <dev/ed/if_edreg.h>
+#include <dev/ed/if_edvar.h>
+
+/*
+ * Interrupt conversion table for WD/SMC ASIC/83C584
+ */
+static uint16_t ed_intr_val[] = {
+ 9,
+ 3,
+ 5,
+ 7,
+ 10,
+ 11,
+ 15,
+ 4
+};
+
+/*
+ * Interrupt conversion table for 83C790
+ */
+static uint16_t ed_790_intr_val[] = {
+ 0,
+ 9,
+ 3,
+ 5,
+ 7,
+ 10,
+ 11,
+ 15
+};
+
+static void
+ed_disable_16bit_access(struct ed_softc *sc)
+{
+ /*
+ * Disable 16 bit access to shared memory
+ */
+ if (sc->isa16bit) {
+ if (sc->chip_type == ED_CHIP_TYPE_WD790)
+ ed_asic_outb(sc, ED_WD_MSR, 0x00);
+ ed_asic_outb(sc, ED_WD_LAAR, sc->wd_laar_proto &
+ ~ED_WD_LAAR_M16EN);
+ }
+}
+
+/*
+ * Probe and vendor-specific initialization routine for SMC/WD80x3 boards
+ */
+int
+ed_probe_WD80x3_generic(device_t dev, int flags, uint16_t *intr_vals[])
+{
+ struct ed_softc *sc = device_get_softc(dev);
+ int error;
+ int i;
+ u_int memsize;
+ u_char iptr, isa16bit, sum, totalsum;
+ u_long irq, junk, pmem;
+
+ sc->chip_type = ED_CHIP_TYPE_DP8390;
+
+ if (ED_FLAGS_GETTYPE(flags) == ED_FLAGS_TOSH_ETHER) {
+ totalsum = ED_WD_ROM_CHECKSUM_TOTAL_TOSH_ETHER;
+ ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_POW);
+ DELAY(10000);
+ }
+ else
+ totalsum = ED_WD_ROM_CHECKSUM_TOTAL;
+
+ /*
+ * Attempt to do a checksum over the station address PROM. If it
+ * fails, it's probably not a SMC/WD board. There is a problem with
+ * this, though: some clone WD boards don't pass the checksum test.
+ * Danpex boards for one.
+ */
+ for (sum = 0, i = 0; i < 8; ++i)
+ sum += ed_asic_inb(sc, ED_WD_PROM + i);
+
+ if (sum != totalsum) {
+
+ /*
+ * Checksum is invalid. This often happens with cheap WD8003E
+ * clones. In this case, the checksum byte (the eighth byte)
+ * seems to always be zero.
+ */
+ if (ed_asic_inb(sc, ED_WD_CARD_ID) != ED_TYPE_WD8003E ||
+ ed_asic_inb(sc, ED_WD_PROM + 7) != 0)
+ return (ENXIO);
+ }
+ /* reset card to force it into a known state. */
+ if (ED_FLAGS_GETTYPE(flags) == ED_FLAGS_TOSH_ETHER)
+ ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW);
+ else
+ ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_RST);
+
+ DELAY(100);
+ ed_asic_outb(sc, ED_WD_MSR, ed_asic_inb(sc, ED_WD_MSR) & ~ED_WD_MSR_RST);
+ /* wait in the case this card is reading its EEROM */
+ DELAY(5000);
+
+ sc->vendor = ED_VENDOR_WD_SMC;
+ sc->type = ed_asic_inb(sc, ED_WD_CARD_ID);
+
+ /*
+ * Set initial values for width/size.
+ */
+ memsize = 8192;
+ isa16bit = 0;
+ switch (sc->type) {
+ case ED_TYPE_WD8003S:
+ sc->type_str = "WD8003S";
+ break;
+ case ED_TYPE_WD8003E:
+ sc->type_str = "WD8003E";
+ break;
+ case ED_TYPE_WD8003EB:
+ sc->type_str = "WD8003EB";
+ break;
+ case ED_TYPE_WD8003W:
+ sc->type_str = "WD8003W";
+ break;
+ case ED_TYPE_WD8013EBT:
+ sc->type_str = "WD8013EBT";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013W:
+ sc->type_str = "WD8013W";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EP: /* also WD8003EP */
+ if (ed_asic_inb(sc, ED_WD_ICR) & ED_WD_ICR_16BIT) {
+ isa16bit = 1;
+ memsize = 16384;
+ sc->type_str = "WD8013EP";
+ } else
+ sc->type_str = "WD8003EP";
+ break;
+ case ED_TYPE_WD8013WC:
+ sc->type_str = "WD8013WC";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EBP:
+ sc->type_str = "WD8013EBP";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EPC:
+ sc->type_str = "WD8013EPC";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_SMC8216C: /* 8216 has 16K shared mem -- 8416 has 8K */
+ case ED_TYPE_SMC8216T:
+ if (sc->type == ED_TYPE_SMC8216C)
+ sc->type_str = "SMC8216/SMC8216C";
+ else
+ sc->type_str = "SMC8216T";
+
+ ed_asic_outb(sc, ED_WD790_HWR,
+ ed_asic_inb(sc, ED_WD790_HWR) | ED_WD790_HWR_SWH);
+ switch (ed_asic_inb(sc, ED_WD790_RAR) & ED_WD790_RAR_SZ64) {
+ case ED_WD790_RAR_SZ64:
+ memsize = 65536;
+ break;
+ case ED_WD790_RAR_SZ32:
+ memsize = 32768;
+ break;
+ case ED_WD790_RAR_SZ16:
+ memsize = 16384;
+ break;
+ case ED_WD790_RAR_SZ8:
+ /* 8216 has 16K shared mem -- 8416 has 8K */
+ if (sc->type == ED_TYPE_SMC8216C)
+ sc->type_str = "SMC8416C/SMC8416BT";
+ else
+ sc->type_str = "SMC8416T";
+ memsize = 8192;
+ break;
+ }
+ ed_asic_outb(sc, ED_WD790_HWR,
+ ed_asic_inb(sc, ED_WD790_HWR) & ~ED_WD790_HWR_SWH);
+
+ isa16bit = 1;
+ sc->chip_type = ED_CHIP_TYPE_WD790;
+ break;
+ case ED_TYPE_TOSHIBA1:
+ sc->type_str = "Toshiba1";
+ memsize = 32768;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_TOSHIBA4:
+ sc->type_str = "Toshiba4";
+ memsize = 32768;
+ isa16bit = 1;
+ break;
+ default:
+ sc->type_str = "";
+ break;
+ }
+
+ /*
+ * Make some adjustments to initial values depending on what is found
+ * in the ICR.
+ */
+ if (isa16bit && (sc->type != ED_TYPE_WD8013EBT)
+ && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4)
+ && ((ed_asic_inb(sc, ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) {
+ isa16bit = 0;
+ memsize = 8192;
+ }
+
+ /* Override memsize? XXX */
+ error = ed_alloc_memory(dev, 0, memsize);
+ if (error)
+ return (error);
+ sc->mem_start = (caddr_t) rman_get_virtual(sc->mem_res);
+
+#ifdef ED_DEBUG
+ printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%lu\n",
+ sc->type, sc->type_str, isa16bit, memsize,
+ rman_get_size(sc->mem_res));
+ for (i = 0; i < 8; i++)
+ printf("%x -> %x\n", i, ed_asic_inb(sc, i));
+#endif
+ pmem = rman_get_start(sc->mem_res);
+ error = ed_isa_mem_ok(dev, pmem, memsize);
+ if (error)
+ return (error);
+
+ /*
+ * (note that if the user specifies both of the following flags that
+ * '8bit' mode intentionally has precedence)
+ */
+ if (flags & ED_FLAGS_FORCE_16BIT_MODE)
+ isa16bit = 1;
+ if (flags & ED_FLAGS_FORCE_8BIT_MODE)
+ isa16bit = 0;
+
+ /*
+ * If possible, get the assigned interrupt number from the card and
+ * use it.
+ */
+ if ((sc->type & ED_WD_SOFTCONFIG) &&
+ (sc->chip_type != ED_CHIP_TYPE_WD790)) {
+
+ /*
+ * Assemble together the encoded interrupt number.
+ */
+ iptr = (ed_asic_inb(sc, ED_WD_ICR) & ED_WD_ICR_IR2) |
+ ((ed_asic_inb(sc, ED_WD_IRR) &
+ (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5);
+
+ /*
+ * If no interrupt specified (or "?"), use what the board tells us.
+ */
+ error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, &junk);
+ if (error && intr_vals[0] != NULL)
+ error = bus_set_resource(dev, SYS_RES_IRQ, 0,
+ intr_vals[0][iptr], 1);
+ if (error)
+ return (error);
+
+ /*
+ * Enable the interrupt.
+ */
+ ed_asic_outb(sc, ED_WD_IRR,
+ ed_asic_inb(sc, ED_WD_IRR) | ED_WD_IRR_IEN);
+ }
+ if (sc->chip_type == ED_CHIP_TYPE_WD790) {
+ ed_asic_outb(sc, ED_WD790_HWR,
+ ed_asic_inb(sc, ED_WD790_HWR) | ED_WD790_HWR_SWH);
+ iptr = (((ed_asic_inb(sc, ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) |
+ (ed_asic_inb(sc, ED_WD790_GCR) &
+ (ED_WD790_GCR_IR1 | ED_WD790_GCR_IR0)) >> 2);
+ ed_asic_outb(sc, ED_WD790_HWR,
+ ed_asic_inb(sc, ED_WD790_HWR) & ~ED_WD790_HWR_SWH);
+
+ /*
+ * If no interrupt specified (or "?"), use what the board tells us.
+ */
+ error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, &junk);
+ if (error && intr_vals[1] != NULL)
+ error = bus_set_resource(dev, SYS_RES_IRQ, 0,
+ intr_vals[1][iptr], 1);
+ if (error)
+ return (error);
+
+ /*
+ * Enable interrupts.
+ */
+ ed_asic_outb(sc, ED_WD790_ICR,
+ ed_asic_inb(sc, ED_WD790_ICR) | ED_WD790_ICR_EIL);
+ }
+ error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, &junk);
+ if (error) {
+ device_printf(dev, "%s cards don't support auto-detected/assigned interrupts.\n",
+ sc->type_str);
+ return (ENXIO);
+ }
+ sc->isa16bit = isa16bit;
+ sc->mem_shared = 1;
+
+ /*
+ * allocate one xmit buffer if < 16k, two buffers otherwise
+ */
+ if (memsize < 16384 || (flags & ED_FLAGS_NO_MULTI_BUFFERING))
+ sc->txb_cnt = 1;
+ else
+ sc->txb_cnt = 2;
+ sc->tx_page_start = ED_WD_PAGE_OFFSET;
+ sc->rec_page_start = ED_WD_PAGE_OFFSET + ED_TXBUF_SIZE * sc->txb_cnt;
+ sc->rec_page_stop = ED_WD_PAGE_OFFSET + memsize / ED_PAGE_SIZE;
+ sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * sc->rec_page_start);
+ sc->mem_size = memsize;
+ sc->mem_end = sc->mem_start + memsize;
+
+ /*
+ * Get station address from on-board ROM
+ */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ sc->arpcom.ac_enaddr[i] = ed_asic_inb(sc, ED_WD_PROM + i);
+
+ /*
+ * Set upper address bits and 8/16 bit access to shared memory.
+ */
+ if (isa16bit) {
+ if (sc->chip_type == ED_CHIP_TYPE_WD790)
+ sc->wd_laar_proto = ed_asic_inb(sc, ED_WD_LAAR);
+ else
+ sc->wd_laar_proto = ED_WD_LAAR_L16EN |
+ ((pmem >> 19) & ED_WD_LAAR_ADDRHI);
+ /*
+ * Enable 16bit access
+ */
+ ed_asic_outb(sc, ED_WD_LAAR, sc->wd_laar_proto |
+ ED_WD_LAAR_M16EN);
+ } else {
+ if (((sc->type & ED_WD_SOFTCONFIG) ||
+ (sc->type == ED_TYPE_TOSHIBA1) ||
+ (sc->type == ED_TYPE_TOSHIBA4) ||
+ (sc->type == ED_TYPE_WD8013EBT)) &&
+ (sc->chip_type != ED_CHIP_TYPE_WD790)) {
+ sc->wd_laar_proto = (pmem >> 19) &
+ ED_WD_LAAR_ADDRHI;
+ ed_asic_outb(sc, ED_WD_LAAR, sc->wd_laar_proto);
+ }
+ }
+
+ /*
+ * Set address and enable interface shared memory.
+ */
+ if (sc->chip_type != ED_CHIP_TYPE_WD790) {
+ if (ED_FLAGS_GETTYPE(flags) == ED_FLAGS_TOSH_ETHER) {
+ ed_asic_outb(sc, ED_WD_MSR + 1,
+ ((pmem >> 8) & 0xe0) | 4);
+ ed_asic_outb(sc, ED_WD_MSR + 2, ((pmem >> 16) & 0x0f));
+ ed_asic_outb(sc, ED_WD_MSR,
+ ED_WD_MSR_MENB | ED_WD_MSR_POW);
+ } else {
+ ed_asic_outb(sc, ED_WD_MSR, ((pmem >> 13) &
+ ED_WD_MSR_ADDR) | ED_WD_MSR_MENB);
+ }
+ sc->cr_proto = ED_CR_RD2;
+ } else {
+ ed_asic_outb(sc, ED_WD_MSR, ED_WD_MSR_MENB);
+ ed_asic_outb(sc, ED_WD790_HWR,
+ (ed_asic_inb(sc, ED_WD790_HWR) | ED_WD790_HWR_SWH));
+ ed_asic_outb(sc, ED_WD790_RAR,
+ ((pmem >> 13) & 0x0f) | ((pmem >> 11) & 0x40) |
+ (ed_asic_inb(sc, ED_WD790_RAR) & 0xb0));
+ ed_asic_outb(sc, ED_WD790_HWR,
+ (ed_asic_inb(sc, ED_WD790_HWR) & ~ED_WD790_HWR_SWH));
+ sc->cr_proto = 0;
+ }
+
+ error = ed_clear_memory(dev);
+ if (error) {
+ ed_disable_16bit_access(sc);
+ return (error);
+ }
+
+ /*
+ * Disable 16bit access to shared memory - we leave it
+ * disabled so that 1) machines reboot properly when the board
+ * is set 16 bit mode and there are conflicting 8bit
+ * devices/ROMS in the same 128k address space as this boards
+ * shared memory. and 2) so that other 8 bit devices with
+ * shared memory can be used in this 128k region, too.
+ */
+ ed_disable_16bit_access(sc);
+ return (0);
+}
+
+int
+ed_probe_WD80x3(device_t dev, int port_rid, int flags)
+{
+ struct ed_softc *sc = device_get_softc(dev);
+ int error;
+ static uint16_t *intr_vals[] = {ed_intr_val, ed_790_intr_val};
+
+ error = ed_alloc_port(dev, port_rid, ED_WD_IO_PORTS);
+ if (error)
+ return (error);
+
+ sc->asic_offset = ED_WD_ASIC_OFFSET;
+ sc->nic_offset = ED_WD_NIC_OFFSET;
+
+ return ed_probe_WD80x3_generic(dev, flags, intr_vals);
+}