summaryrefslogtreecommitdiff
path: root/i386/i386at/gpl/if_ul.c
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /i386/i386at/gpl/if_ul.c
Initial source
Diffstat (limited to 'i386/i386at/gpl/if_ul.c')
-rw-r--r--i386/i386at/gpl/if_ul.c489
1 files changed, 489 insertions, 0 deletions
diff --git a/i386/i386at/gpl/if_ul.c b/i386/i386at/gpl/if_ul.c
new file mode 100644
index 0000000..59e74f5
--- /dev/null
+++ b/i386/i386at/gpl/if_ul.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 1994 Shantanu Goel
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+/*
+ * Written 1993 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may be used and
+ * distributed according to the terms of the GNU Public License,
+ * incorporated herein by reference.
+ *
+ * The Author may be reached as becker@super.org or
+ * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ */
+
+#include <wd.h>
+#include <ul.h>
+#if NUL > 0
+/*
+ * Driver for SMC Ultra ethernet adaptor.
+ * Derived from the Linux driver by Donald Becker.
+ *
+ * Shantanu Goel (goel@cs.columbia.edu)
+ */
+#include <mach/sa/sys/types.h>
+#include "vm_param.h"
+#include <kern/time_out.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+#include <chips/busses.h>
+#include <i386/machspl.h>
+#include <i386/pio.h>
+#include <i386at/gpl/if_nsreg.h>
+
+#define START_PG 0x00 /* first page of TX buffer */
+#define ULTRA_CMDREG 0 /* offset of ASIC command register */
+#define ULTRA_RESET 0x80 /* board reset in ULTRA_CMDREG */
+#define ULTRA_MEMEN 0x40 /* enable shared memory */
+#define ULTRA_NIC_OFF 16 /* NIC register offset */
+
+#define ulunit(dev) minor(dev)
+
+/*
+ * Autoconfiguration stuff.
+ */
+int ulprobe();
+void ulattach();
+int ulstd[] = { 0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0 };
+struct bus_device *ulinfo[NUL];
+struct bus_driver uldriver = {
+ ulprobe, 0, ulattach, 0, ulstd, "ul", ulinfo, 0, 0, 0
+};
+
+/*
+ * NS8390 state.
+ */
+struct nssoftc ulnssoftc[NUL];
+
+/*
+ * Ultra state.
+ */
+struct ulsoftc {
+ int sc_mstart; /* start of board's RAM */
+ int sc_mend; /* end of board's RAM */
+ int sc_rmstart; /* start of receive RAM */
+ int sc_rmend; /* end of receive RAM */
+} ulsoftc[NUL];
+
+void ulstart(int);
+void ul_reset(struct nssoftc *sc);
+void ul_input(struct nssoftc *sc, int, char *, int);
+int ul_output(struct nssoftc *sc, int, char *, int);
+
+/*
+ * Watchdog.
+ */
+int ulwstart = 0;
+void ulwatch(void);
+
+#define ULDEBUG
+#ifdef ULDEBUG
+int uldebug = 0;
+#define DEBUGF(stmt) { if (uldebug) stmt; }
+#else
+#define DEBUGF(stmt)
+#endif
+
+/*
+ * Probe for the Ultra.
+ * This looks like an 8013 with the station address PROM
+ * at I/O ports <base>+8 to <base>+13, with a checksum following.
+ */
+int
+ulprobe(xxx, ui)
+ int xxx;
+ struct bus_device *ui;
+{
+ int *port;
+
+ if (ui->unit >= NUL) {
+ printf("ul%d: not configured\n", ui->unit);
+ return (0);
+ }
+ for (port = ulstd; *port; port++) {
+ if (*port < 0)
+ continue;
+ /*
+ * Check chip ID nibble.
+ */
+ if ((inb(*port + 7) & 0xf0) != 0x20)
+ continue;
+ if (ulprobe1(*port, ui)) {
+ ui->address = *port;
+ *port = -1;
+#if NWD > 0
+ /*
+ * XXX: The Western Digital/SMC driver can sometimes
+ * probe the Ultra incorrectly. Remove the Ultra's
+ * port from it's list to avoid the problem.
+ */
+ {
+ int i;
+ extern int wdstd[];
+
+ for (i = 0; wdstd[i]; i++) {
+ if (wdstd[i] == ui->address) {
+ wdstd[i] = -1;
+ break;
+ }
+ }
+ }
+#endif
+ return (1);
+ }
+ }
+ return (0);
+}
+
+int
+ulprobe1(port, ui)
+ int port;
+ struct bus_device *ui;
+{
+ u_char num_pages, irqreg, addr, reg4;
+ u_char irqmap[] = { 0, 9, 3, 5, 7, 10, 11, 15 };
+ short num_pages_tbl[4] = { 0x20, 0x40, 0x80, 0xff };
+ int i, irq, checksum = 0;
+ int addr_tbl[4] = { 0x0c0000, 0x0e0000, 0xfc0000, 0xfe0000 };
+ struct ulsoftc *ul = &ulsoftc[ui->unit];
+ struct nssoftc *ns = &ulnssoftc[ui->unit];
+ struct ifnet *ifp = &ns->sc_if;
+
+ /*
+ * Select the station address register set.
+ */
+ reg4 = inb(port + 4) & 0x7f;
+ outb(port + 4, reg4);
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(port + 8 + i);
+ if ((checksum & 0xff) != 0xff)
+ return (0);
+
+ /*
+ * Use 2 transmit buffers.
+ */
+ ns->sc_pingpong = 1;
+
+ printf("ul%d: SMC Ultra at 0x%03x, ", ui->unit, port);
+ for (i = 0; i < ETHER_ADDR_LEN; i++) {
+ if (i == 0)
+ printf("%02x", ns->sc_addr[i] = inb(port + 8 + i));
+ else
+ printf(":%02x", ns->sc_addr[i] = inb(port + 8 + i));
+ }
+ /*
+ * Switch from station address to alternate register set
+ * and read useful registers there.
+ */
+ outb(port + 4, 0x80 | reg4);
+
+ /*
+ * Enable FINE16 mode to avoid BIOS ROM width mismatches
+ * during reboot.
+ */
+ outb(port + 0x0c, 0x80 | inb(port + 0x0c));
+ irqreg = inb(port + 0x0d);
+ addr = inb(port + 0x0b);
+
+ /*
+ * Switch back to station address register set so the MSDOG
+ * driver can find the card after a warm boot.
+ */
+ outb(port + 4, reg4);
+
+ /*
+ * Determine IRQ. The IRQ bits are split.
+ */
+ irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
+ if (irq == 0) {
+ printf(", failed to detect IRQ line.\n");
+ return (0);
+ }
+ ui->sysdep1 = irq;
+ take_dev_irq(ui);
+ printf(", irq %d", irq);
+
+ /*
+ * Determine board's RAM location.
+ */
+ ul->sc_mstart = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3];
+ num_pages = num_pages_tbl[(addr >> 4) & 3];
+ ul->sc_rmstart = ul->sc_mstart + TX_PAGES(ns) * 256;
+ ul->sc_mend = ul->sc_rmend
+ = ul->sc_mstart + (num_pages - START_PG) * 256;
+ printf(", memory 0x%05x-0x%05x\n", ul->sc_mstart, ul->sc_mend);
+
+ /*
+ * Initialize 8390 state.
+ */
+ ns->sc_name = ui->name;
+ ns->sc_unit = ui->unit;
+ ns->sc_port = port + ULTRA_NIC_OFF;
+ ns->sc_word16 = 1;
+ ns->sc_txstrtpg = START_PG;
+ ns->sc_rxstrtpg = START_PG + TX_PAGES(ns);
+ ns->sc_stoppg = num_pages;
+ ns->sc_reset = ul_reset;
+ ns->sc_input = ul_input;
+ ns->sc_output = ul_output;
+
+ DEBUGF(printf("ul%d: txstrtpg %d rxstrtpg %d num_pages %d\n",
+ ui->unit, ns->sc_txstrtpg, ns->sc_rxstrtpg, num_pages));
+
+ /*
+ * Initialize interface header.
+ */
+ ifp->if_unit = ui->unit;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST;
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = ETHER_ADDR_LEN;
+ ifp->if_address = ns->sc_addr;
+ if_init_queues(ifp);
+
+ return (1);
+}
+
+void
+ulattach(ui)
+ struct bus_device *ui;
+{
+ /*
+ * void
+ */
+}
+
+int
+ulopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ int unit = ulunit(dev), s;
+ struct bus_device *ui;
+
+ if (unit >= NUL || (ui = ulinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ /*
+ * Start watchdog.
+ */
+ if (!ulwstart) {
+ ulwstart++;
+ timeout(ulwatch, 0, hz);
+ }
+ ulnssoftc[unit].sc_if.if_flags |= IFF_UP;
+ s = splimp();
+ outb(ui->address, ULTRA_MEMEN); /* enable memory, 16 bit mode */
+ outb(ui->address + 5, 0x80);
+ outb(ui->address + 6, 0x01); /* enable interrupts and memory */
+ nsinit(&ulnssoftc[unit]);
+ splx(s);
+ return (0);
+}
+
+int
+uloutput(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ int unit = ulunit(dev);
+ struct bus_device *ui;
+
+ if (unit >= NUL || (ui = ulinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_write(&ulnssoftc[unit].sc_if, ulstart, ior));
+}
+
+int
+ulsetinput(dev, receive_port, priority, filter, filter_count)
+ dev_t dev;
+ mach_port_t receive_port;
+ int priority;
+ filter_t *filter;
+ unsigned filter_count;
+{
+ int unit = ulunit(dev);
+ struct bus_device *ui;
+
+ if (unit >= NUL || (ui = ulinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_set_filter(&ulnssoftc[unit].sc_if, receive_port,
+ priority, filter, filter_count));
+}
+
+int
+ulgetstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned *count;
+{
+ int unit = ulunit(dev);
+ struct bus_device *ui;
+
+ if (unit >= NUL || (ui = ulinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_getstat(&ulnssoftc[unit].sc_if, flavor, status, count));
+}
+
+int
+ulsetstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned count;
+{
+ int unit = ulunit(dev), oflags, s;
+ struct bus_device *ui;
+ struct ifnet *ifp;
+ struct net_status *ns;
+
+ if (unit >= NUL || (ui = ulinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ ifp = &ulnssoftc[unit].sc_if;
+
+ switch (flavor) {
+
+ case NET_STATUS:
+ if (count < NET_STATUS_COUNT)
+ return (D_INVALID_SIZE);
+ ns = (struct net_status *)status;
+ oflags = ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC);
+ ifp->if_flags &= ~(IFF_ALLMULTI|IFF_PROMISC);
+ ifp->if_flags |= ns->flags & (IFF_ALLMULTI|IFF_PROMISC);
+ if ((ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC)) != oflags) {
+ s = splimp();
+ nsinit(&ulnssoftc[unit]);
+ splx(s);
+ }
+ break;
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+
+void
+ulintr(unit)
+ int unit;
+{
+ nsintr(&ulnssoftc[unit]);
+}
+
+void
+ulstart(unit)
+ int unit;
+{
+ nsstart(&ulnssoftc[unit]);
+}
+
+void
+ul_reset(ns)
+ struct nssoftc *ns;
+{
+ int port = ns->sc_port - ULTRA_NIC_OFF; /* ASIC base address */
+
+ outb(port, ULTRA_RESET);
+ outb(0x80, 0); /* I/O delay */
+ outb(port, ULTRA_MEMEN);
+}
+
+void
+ul_input(ns, count, buf, ring_offset)
+ struct nssoftc *ns;
+ int count;
+ char *buf;
+ int ring_offset;
+{
+ int xfer_start;
+ struct ulsoftc *ul = &ulsoftc[ns->sc_unit];
+
+ DEBUGF(printf("ul%d: ring_offset = %d\n", ns->sc_unit, ring_offset));
+
+ xfer_start = ul->sc_mstart + ring_offset - (START_PG << 8);
+ if (xfer_start + count > ul->sc_rmend) {
+ int semi_count = ul->sc_rmend - xfer_start;
+
+ /*
+ * Input move must be wrapped.
+ */
+ bcopy((char *)phystokv(xfer_start), buf, semi_count);
+ count -= semi_count;
+ bcopy((char *)phystokv(ul->sc_rmstart), buf+semi_count, count);
+ } else
+ bcopy((char *)phystokv(xfer_start), buf, count);
+}
+
+int
+ul_output(ns, count, buf, start_page)
+ struct nssoftc *ns;
+ int count;
+ char *buf;
+ int start_page;
+{
+ char *shmem;
+ int i;
+ struct ulsoftc *ul = &ulsoftc[ns->sc_unit];
+
+ DEBUGF(printf("ul%d: start_page = %d\n", ns->sc_unit, start_page));
+
+ shmem = (char *)phystokv(ul->sc_mstart + ((start_page-START_PG) << 8));
+ bcopy(buf, shmem, count);
+ while (count < ETHERMIN + sizeof(struct ether_header)) {
+ *(shmem + count) = 0;
+ count++;
+ }
+ return (count);
+}
+
+/*
+ * Watchdog.
+ * Check for hung transmissions.
+ */
+void
+ulwatch()
+{
+ int unit, s;
+ struct nssoftc *ns;
+
+ timeout(ulwatch, 0, hz);
+
+ s = splimp();
+ for (unit = 0; unit < NUL; unit++) {
+ if (ulinfo[unit] == 0 || ulinfo[unit]->alive == 0)
+ continue;
+ ns = &ulnssoftc[unit];
+ if (ns->sc_timer && --ns->sc_timer == 0) {
+ printf("ul%d: transmission timeout\n", unit);
+ nsinit(ns);
+ }
+ }
+ splx(s);
+}
+
+#endif /* NUL > 0 */