|
|
GNU Mach 1.0
/*
* 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 [email protected] 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 ([email protected])
*/
#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 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.