diff options
author | Thomas Bushnell <thomas@gnu.org> | 1997-02-25 21:28:37 +0000 |
---|---|---|
committer | Thomas Bushnell <thomas@gnu.org> | 1997-02-25 21:28:37 +0000 |
commit | f07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch) | |
tree | 12b07c7e578fc1a5f53dbfde2632408491ff2a70 /chips/lance.c |
Initial source
Diffstat (limited to 'chips/lance.c')
-rw-r--r-- | chips/lance.c | 1570 |
1 files changed, 1570 insertions, 0 deletions
diff --git a/chips/lance.c b/chips/lance.c new file mode 100644 index 0000000..750d4c5 --- /dev/null +++ b/chips/lance.c @@ -0,0 +1,1570 @@ +/* + * Mach Operating System + * Copyright (c) 1993-1989 Carnegie Mellon University + * 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. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * File: lance.c + * Author: Robert V. Baron & Alessandro Forin + * Date: 5/90 + * + * Driver for the DEC LANCE Ethernet Controller. + */ + +/* + + Byte ordering issues. + + The lance sees data naturally as half word (16 bit) quantitites. + Bit 2 (BSWP) in control register 3 (CSR3) controls byte swapping. + To quote the spec: + + 02 BSWP BYTE SWAP allows the chip to + operate in systems that consdier bits (15:08) of data pointers + by an even addressa and bits (7:0) to be pointed by an + odd address. + + When BSWP=1, the chip will swap the high and low bytes on DMA + data transfers between the silo and bus memory. Only data from + silo transfers is swapped; the Initialization Block data and + the Descriptor Ring entries are NOT swapped. (emphasis theirs) + + + So on systems with BYTE_MSF=1, the BSWP bit should be set. Note, + however, that all shorts in the descriptor ring and initialization + block need to be swapped. The BITFIELD macros in lance.h handle this + magic. + +*/ + +#include <ln.h> +#if NLN > 0 +#include <platforms.h> + +/* + * AMD Am7990 LANCE (Ethernet Interface) + */ +#include <sys/ioctl.h> +#include <vm/vm_kern.h> + +#include <machine/machspl.h> /* spl definitions */ +#include <kern/time_out.h> +#include <sys/syslog.h> +#include <ipc/ipc_port.h> +#include <ipc/ipc_kmsg.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> + +#ifdef FLAMINGO +#define se_reg_type unsigned int +#endif + +#include <chips/lance.h> +#include <chips/busses.h> + +#define private static +#define public + +typedef struct se_softc *se_softc_t; /* move above prototypes */ + +void se_write_reg(); /* forwards */ +void se_read(); +void se_rint(); +void se_tint(); + +private vm_offset_t se_Hmem_nogap(), se_Hmem_gap16(); +private vm_offset_t se_malloc(); + + +/* This config section should go into a separate file */ + +#ifdef LUNA88K +# include <luna88k/board.h> +# define MAPPED 1 + #undef bcopy + extern void bcopy(), bzero(); + +#define wbflush() +#define Hmem(lna) (vm_offset_t)((lna) + sc->lnbuf) +#define Lmem(lna) (vm_offset_t)((lna) + sc->lnoffset) + +#define SPACE (TRI_PORT_RAM_SPACE>>1) +private struct se_switch se_switch[] = { + { LANCE_ADDR - TRI_PORT_RAM, /* pointer */ + SPACE /* host side */, + SPACE /* lance side */, + - TRI_PORT_RAM, + 0, /* romstride */ + 0, /* ramstride */ + SPACE, + /* desc_copyin */ bcopy, + /* desc_copyout */ bcopy, + /* data_copyin */ bcopy, + /* data_copyout */ bcopy, + /* bzero */ bzero, + /* mapaddr */ se_Hmem_nogap, + /* mapoffs */ se_Hmem_nogap + }, +}; + +#endif + +#ifdef DECSTATION +#include <mips/mips_cpu.h> +#include <mips/PMAX/pmad_aa.h> + +#define MAPPED 1 + +/* + * The LANCE buffer memory as seen from the Pmax cpu is funny. + * It is viewed as short words (16bits), spaced at word (32bits) + * intervals. The same applies to the registers. From the LANCE + * point of view memory is instead contiguous. + * The ROM that contains the station address is in the space belonging + * to the clock/battery backup memory. This space is again 16 bits + * in a 32bit envelope. And the ether address is stored in the "high" + * byte of 6 consecutive quantities. + * + * But Pmaxen and 3maxen (and..) map lance space differently. + * This requires dynamic adaptation of the driver, which + * is done via the following switches. + * For convenience, the switch holds information about + * the location of the lance control registers as well. + * This could be either absolute (pmax) or relative to + * some register base (3max, turbochannel) + */ +void copyin_gap16(), copyout_gap16(), bzero_gap16(); +extern void bcopy(), bzero(); +void copyin_gap32(), copyout_gap32(); + +private struct se_switch se_switch[] = { +/* pmax */ + { 0x00000000, 0x01000000, 0x0, 0x05000000, 8, 16, 64*1024, + copyin_gap16, copyout_gap16, copyin_gap16, copyout_gap16, + bzero_gap16, se_Hmem_gap16, se_Hmem_gap16}, +/* 3max */ + { PMAD_OFFSET_LANCE, PMAD_OFFSET_RAM, PMAD_OFFSET_RAM, PMAD_OFFSET_ROM, + 16, 0, PMAD_RAM_SIZE, + bcopy, bcopy, bcopy, bcopy, bzero, se_Hmem_nogap, se_Hmem_nogap}, +/* 3min */ +/* XXX re-use other 64k */ + { 0/*later*/, 0/*later*/, 0x0, 0/*later*/, 0, 128, 64*1024, + copyin_gap16, copyout_gap16, copyin_gap32, copyout_gap32, + bzero_gap16, se_Hmem_gap16, se_Hmem_nogap}, +}; + +/* + * "lna" is what se_malloc hands back. They are offsets using + * the sizing that the Lance would use. The Lance space is + * mapped somewhere in the I/O space, as indicated by the softc. + * Hence we have these two macros: + */ +/* H & L are not hi and lo but + H = HOST == addresses for host to reference board memory + L = LOCAL == addresses on board + */ +#define Hmem(lna) (vm_offset_t)((se_sw->mapaddr)(lna) + sc->lnbuf) +#define Lmem(lna) (vm_offset_t)((vm_offset_t)lna + sc->lnoffset) +#endif /*DECSTATION*/ + + +#ifdef VAXSTATION +#include <vax/ka3100.h> + +#define wbflush() + +void xzero(x, l) vm_offset_t x; int l; { blkclr(x, l); } +void xcopy(f, t, l) vm_offset_t f, t; int l; { bcopy(f, t, l); } + +private struct se_switch se_switch[] = { + /* pvax sees contiguous bits in lower 16Meg of memory */ + { 0, 0, 0, 0, 0, 0, 64*1024, + xcopy, xcopy, xcopy, xcopy, xzero, se_Hmem_nogap, se_Hmem_nogap}, +}; + +/* + * "lna" is what se_malloc hands back. They are offsets using + * the sizing that the Lance would use. The Lance space is + * mapped somewhere in the I/O space, as indicated by the softc. + * Hence we have these two macros: + */ +/* H & L are not hi and lo but + H = HOST == addresses for host to reference board memory + L = LOCAL == addresses on board + */ + /* + * This does not deal with > 16 Meg physical memory, where + * Hmem != Lmem + */ +#define Hmem(lna) (vm_offset_t)((lna) + sc->lnbuf) +#define Lmem(lna) (vm_offset_t)((lna) + sc->lnoffset) + +#endif /*VAXSTATION*/ + + +#ifdef FLAMINGO +#include <alpha/alpha_cpu.h> + +/* XXX might be wrong, mostly stolen from kmin */ +extern void copyin_gap16(), copyout_gap16(), bzero_gap16(); +extern void copyin_gap32(), copyout_gap32(); +extern void bcopy(), bzero(); + +private struct se_switch se_switch[] = { +/* XXX re-use other 64k */ + { 0/*later*/, 0/*later*/, 0x0, 0/*later*/, 0, 128, 64*1024, + copyin_gap16, copyout_gap16, copyin_gap32, copyout_gap32, + bzero_gap16, se_Hmem_gap16, se_Hmem_nogap}, +}; + +/* + * "lna" is what se_malloc hands back. They are offsets using + * the sizing that the Lance would use. The Lance space is + * mapped somewhere in the I/O space, as indicated by the softc. + * Hence we have these two macros: + */ +/* H & L are not hi and lo but + H = HOST == addresses for host to reference board memory + L = LOCAL == addresses on board + */ +#define Hmem(lna) (vm_offset_t)((se_sw->mapaddr)(lna) + sc->lnbuf) +#define Lmem(lna) (vm_offset_t)((vm_offset_t)lna + sc->lnoffset) +#endif /*FLAMINGO*/ + + +/* + * Map a lance-space offset into an host-space one + */ +private vm_offset_t se_Hmem_nogap( vm_offset_t lna) { return lna;} +private vm_offset_t se_Hmem_gap16( vm_offset_t lna) { return lna << 1;} + +/* + * Memory addresses for LANCE are 24 bits wide. + */ +#define Addr_lo(y) ((unsigned short)((vm_offset_t)(y) & 0xffff)) +#define Addr_hi(y) ((unsigned short)(((vm_offset_t)(y)>>16) & 0xff)) + +#define LN_MEMORY_SIZE (se_sw->ramsize) + +/* XXX to accomodate heterogeneity this should be made per-drive */ +/* XXX and then some more */ + +struct se_switch *se_sw = se_switch; + +void set_se_switch(n) +int n; +{ + se_sw = &se_switch[n]; +} + +#ifndef LUNA88K +void setse_switch(n, r, b, l, o) + vm_offset_t r, b, l, o; + int n; +{ + se_switch[n].regspace = r; + se_switch[n].bufspace = b; + se_switch[n].ln_bufspace = l; + se_switch[n].romspace = o; + + /* make sure longword aligned */ + if (se_switch[n].bufspace & 0x7) { + se_switch[n].bufspace = (se_switch[n].bufspace+0x7) & ~0x7; + } + + set_se_switch(n); +} +#endif + +/* + * Autoconf info + */ + +private vm_offset_t se_std[NLN] = { 0 }; +private struct bus_device *se_info[NLN]; +private int se_probe(); +private void se_attach(); + +struct bus_driver se_driver = + { se_probe, 0, se_attach, 0, se_std, "se", se_info, }; + +/* + * Externally visible functions + */ +char *se_unprobed_addr = 0; +void se_intr(); /* kernel */ + +int se_open(), se_output(), se_get_status(), /* user */ + se_set_status(), se_setinput(), se_restart(); + +/* + * + * Internal functions & definitions + * + */ + +private int se_probe(); +private void se_init(); +private void init_lance_space(); +private void se_desc_set_status(); +private volatile long *se_desc_alloc(); /* must be aligned! */ +void se_start(); +private void copy_from_lance(); +private int copy_to_lance(); + +int se_verbose = 0; /* debug flag */ + +#define RLOG 4 /* 2**4 = 16 receive descriptors */ +#define TLOG 4 /* 2**4 = 16 transmit descriptors */ +#define NRCV (1<<RLOG) /* Receive descriptors */ +#define NXMT (1<<TLOG) /* Transmit descriptors */ + +#define LN_BUFFER_SIZE (0x800-0x80) + +/* + * Ethernet software status per interface. + * + * Each interface is referenced by a network interface structure, + * is_if, which contains the output queue for the interface, its address, ... + */ +int se_loopback_hack = 1; + +struct se_softc { + struct ifnet is_if; /* generic interface header */ + unsigned char is_addr[6]; /* ethernet hardware address */ + unsigned short pad; + se_reg_t lnregs; /* Lance registers */ + vm_offset_t lnbuf; /* Lance memory, Host offset */ + vm_offset_t lnoffset; /* Lance memory, Lance offset */ + vm_offset_t lnrom; + vm_offset_t lnsbrk; /* Lance memory allocator */ + vm_offset_t lninit_block; /* Init block address */ + se_desc_t lnrring[NRCV]; /* Receive ring desc. */ + volatile long *lnrbuf[NRCV]; /* Receive buffers */ + se_desc_t lntring[NXMT]; /* Transmit ring desc. */ + volatile long *lntbuf[NXMT]; /* Transmit buffers */ + + int rcv_last; /* Rcv buffer last read */ + + io_req_t tpkt[NXMT+1]; /* Xmt pkt queue */ + int xmt_count; /* Xmt queue size */ + int xmt_last; /* Xmt queue head (insert) */ + int xmt_complete; /* Xmt queue tail (remove) */ + + int se_flags; /* Flags for SIOCSIFFLAGS */ + int counters[4]; /* error counters */ +#define bablcnt counters[0] +#define misscnt counters[1] +#define merrcnt counters[2] +#define rstrtcnt counters[3] +} se_softc_data[NLN]; + +se_softc_t se_softc[NLN]; /* quick access */ + +/* + * Probe the Lance to see if it's there + */ +private int se_open_state = 0; + +private int se_probe( + vm_offset_t reg, + register struct bus_device *ui) +{ + register se_softc_t sc; + se_reg_t rdp, rap; + int unit = ui->unit; + + /* + * See if the interface is there by reading the lance CSR. On pmaxen + * and 3maxen this is superfluous, but.. + */ + rdp = (se_reg_t) (reg + se_sw->regspace); +#ifdef DECSTATION + if (check_memory(rdp, 0)) + return 0; +#endif /*DECSTATION*/ +#ifdef MAPPED + SE_probe(reg,ui); +#endif /*MAPPED*/ + rap = rdp + 2; /* XXX might not be true in the future XXX */ + /* rdp and rap are "shorts" on consecutive + "long" word boundaries */ + + /* + * Bind this interface to the softc. + */ + sc = &se_softc_data[unit]; + se_softc[unit] = sc; + sc->lnregs = (se_reg_t) (reg + se_sw->regspace); + sc->lnbuf = (vm_offset_t) (reg + se_sw->bufspace); + sc->lnoffset = (vm_offset_t) (se_sw->ln_bufspace); + sc->lnrom = (vm_offset_t) (reg + se_sw->romspace); + + /* + * Reset the interface, and make sure we really do it! (the 3max + * seems quite stubborn about these registers) + */ + se_write_reg(rap, CSR0_SELECT, CSR0_SELECT, "RAP"); + se_write_reg(rdp, LN_CSR0_STOP, LN_CSR0_STOP, "csr0"); + + /* + * Allocate lance RAM buffer memory + */ + init_lance_space(sc); + + /* + * Initialize the chip + * + * NOTE: From now on we will only touch csr0 + */ + if (se_ship_init_block(sc, unit)) + return 0; + + /* + * Tell the world we are alive and well + */ + se_open_state++; + return 1; +} + +int se_ship_init_block( + register se_softc_t sc, + int unit) +{ + se_reg_t rdp = sc->lnregs; + se_reg_t rap; + register int i = 0; + + rap = rdp + 2; /* XXX might not be true in the future XXX */ + + /* + * Load LANCE control block. + */ + +#ifdef LUNA88K + /* turn on byte swap bit in csr3, set bcon bit - as in 2.5 */ + se_write_reg(rap, CSR3_SELECT, CSR3_SELECT, "RAP"); + se_write_reg(rdp, LN_CSR3_BSWP|LN_CSR3_BCON, + LN_CSR3_BSWP|LN_CSR3_BCON, "csr3"); +#endif + + se_write_reg(rap, CSR1_SELECT, CSR1_SELECT, "RAP"); + se_write_reg(rdp, Addr_lo(Lmem(sc->lninit_block)), + Addr_lo(Lmem(sc->lninit_block)), "csr1"); + + se_write_reg(rap, CSR2_SELECT, CSR2_SELECT, "RAP"); + se_write_reg(rdp, Addr_hi(Lmem(sc->lninit_block)), + Addr_hi(Lmem(sc->lninit_block)), "csr2"); + + /* + * Start the INIT sequence now + */ + se_write_reg(rap, CSR0_SELECT, CSR0_SELECT, "RAP"); + *rdp = (LN_CSR0_IDON | LN_CSR0_INIT); + wbflush(); + + /* give it plenty of time to settle */ + while (i++ < 10000) { + delay(100); + if ((*rdp & LN_CSR0_IDON) != 0) + break; + } + /* make sure got out okay */ + if ((*rdp & LN_CSR0_IDON) == 0) { + printf("se%d: cannot initialize\n", unit); + if (*rdp & LN_CSR0_ERR) + printf("se%d: initialization error, csr = %04x\n", + unit, (*rdp & 0xffff)); + return 1; + } + /* + * Do not enable interrupts just yet. + */ + /* se_write_reg(rdp, LN_CSR0_STOP, LN_CSR0_STOP, "csr0"); */ + + return 0; +} + +void +se_write_reg( + register se_reg_t regptr, + register int val, + register int result, + char *regname) +{ + register int i = 0; + + while ((unsigned short)(*regptr) != (unsigned short)result) { + *regptr = (se_reg_type)val; + wbflush(); + if (++i > 10000) { + printf("se: %s did not settle (to x%x): x%x\n", + regname, result, (unsigned short)(*regptr)); + return; + } + delay(100); + } +} + +unsigned short +se_read_reg( + register se_reg_t regptr) +{ + return (unsigned short) (*regptr); +} + +private void +init_lance_space( + register se_softc_t sc) +{ + register int lptr; /* Generic lance pointer */ + se_desc_t ringaddr; + long *rom_eaddress = (long *) sc->lnrom; + int i; + struct se_init_block init_block; + + /* + * Allocate local RAM buffer memory for the init block, + * fill in our local copy then copyout. + */ + + sc->lninit_block = se_malloc(sc, sizeof (struct se_init_block)); + + /* + * Set values on stack, then copyout en-masse + */ + bzero(&init_block, sizeof(init_block)); + init_block.mode = 0; + + /* byte swapping between host and lance */ + + init_block.phys_addr_low = ((rom_eaddress[0]>>se_sw->romstride)&0xff) | + (((rom_eaddress[1]>>se_sw->romstride)&0xff) << 8); + init_block.phys_addr_med = ((rom_eaddress[2]>>se_sw->romstride)&0xff) | + (((rom_eaddress[3]>>se_sw->romstride)&0xff) << 8); + init_block.phys_addr_high = ((rom_eaddress[4]>>se_sw->romstride)&0xff) | + (((rom_eaddress[5]>>se_sw->romstride)&0xff) << 8); + + /* + * Allocate both descriptor rings at once. + * Note that the quadword alignment requirement is + * inherent in the way we perform allocation, + * but it does depend on the size of the init block. + */ + lptr = se_malloc(sc, sizeof (struct se_desc) * (NXMT + NRCV)); + + /* + * Initialize the buffer descriptors + */ + init_block.recv_ring_pointer_lo = Addr_lo(Lmem(lptr)); + init_block.recv_ring_pointer_hi = Addr_hi(Lmem(lptr)); + init_block.recv_ring_len = RLOG; + + for ( i = 0; i < NRCV ; i++, lptr += sizeof(struct se_desc)) { + ringaddr = (se_desc_t)Hmem(lptr); + sc->lnrring[i] = ringaddr; + sc->lnrbuf[i] = se_desc_alloc (sc, ringaddr); + } + + init_block.xmit_ring_pointer_lo = Addr_lo(Lmem(lptr)); + init_block.xmit_ring_pointer_hi = Addr_hi(Lmem(lptr)); + init_block.xmit_ring_len = TLOG; + + for ( i = 0 ; i < NXMT ; i++, lptr += sizeof(struct se_desc)) { + ringaddr = (se_desc_t)Hmem(lptr); + sc->lntring[i] = ringaddr; + sc->lntbuf[i] = se_desc_alloc (sc, ringaddr); + } + + /* + * No logical address filtering + */ + init_block.logical_addr_filter0 = 0; + init_block.logical_addr_filter1 = 0; + init_block.logical_addr_filter2 = 0; + init_block.logical_addr_filter3 = 0; + + /* + * Move init block into lance space + */ + (se_sw->desc_copyout)((vm_offset_t)&init_block, Hmem(sc->lninit_block), sizeof(init_block)); + wbflush(); +} + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. + */ +private void +se_attach( + register struct bus_device *ui) +{ + unsigned char *enaddr; + struct ifnet *ifp; + long *rom_eaddress; + int unit = ui->unit; + se_softc_t sc = se_softc[unit]; + + rom_eaddress = (long *) sc->lnrom; + + /* + * Read the address from the prom and save it. + */ + enaddr = sc->is_addr; + enaddr[0] = (unsigned char) ((rom_eaddress[0] >> se_sw->romstride) & 0xff); + enaddr[1] = (unsigned char) ((rom_eaddress[1] >> se_sw->romstride) & 0xff); + enaddr[2] = (unsigned char) ((rom_eaddress[2] >> se_sw->romstride) & 0xff); + enaddr[3] = (unsigned char) ((rom_eaddress[3] >> se_sw->romstride) & 0xff); + enaddr[4] = (unsigned char) ((rom_eaddress[4] >> se_sw->romstride) & 0xff); + enaddr[5] = (unsigned char) ((rom_eaddress[5] >> se_sw->romstride) & 0xff); + + printf(": %x-%x-%x-%x-%x-%x", + (rom_eaddress[0] >> se_sw->romstride) & 0xff, + (rom_eaddress[1] >> se_sw->romstride) & 0xff, + (rom_eaddress[2] >> se_sw->romstride) & 0xff, + (rom_eaddress[3] >> se_sw->romstride) & 0xff, + (rom_eaddress[4] >> se_sw->romstride) & 0xff, + (rom_eaddress[5] >> se_sw->romstride) & 0xff); + + /* + * Initialize the standard interface descriptor + */ + ifp = &sc->is_if; + ifp->if_unit = unit; + ifp->if_header_size = sizeof(struct ether_header); + ifp->if_header_format = HDR_ETHERNET; + ifp->if_address_size = 6; + ifp->if_mtu = ETHERMTU; + ifp->if_flags |= IFF_BROADCAST; + + ifp->if_address = (char *) enaddr; + + if_init_queues(ifp); +#ifdef MAPPED + SE_attach(ui); +#endif /*MAPPED*/ + +} + +/* + * Use a different hardware address for interface + */ +void +se_setaddr( + unsigned char eaddr[6], + int unit) +{ + register se_softc_t sc = se_softc[unit]; + struct se_init_block init_block; + + /* + * Modify initialization block accordingly + */ + (se_sw->desc_copyin) (Hmem(sc->lninit_block), (vm_offset_t)&init_block, sizeof(init_block)); + bcopy(eaddr, &init_block.phys_addr_low, sizeof(*eaddr)); + (se_sw->desc_copyout)((vm_offset_t)&init_block, Hmem(sc->lninit_block), sizeof(init_block)); + /* + * Make a note of it + */ + bcopy(eaddr, sc->is_addr, sizeof(*eaddr)); + + /* + * Restart the interface + */ + se_restart(&sc->is_if); + se_init(unit); +} + +/* + * Restart interface + * + * We use this internally on those errors that hang the chip, + * not sure yet what use the MI code will make of it. + * + * After stopping the chip and effectively turning off the interface + * we release all pending buffers and cause the chip to init + * itself. We do not enable interrupts here. + */ +int +se_restart( register struct ifnet *ifp ) +{ + register se_softc_t sc = se_softc[ifp->if_unit]; + se_reg_t rdp; + register int i; + + rdp = sc->lnregs; + + /* + * stop the chip + */ + se_write_reg(rdp, LN_CSR0_STOP, LN_CSR0_STOP, "csr0"); + + /* + * stop network activity + */ + if (ifp->if_flags & IFF_RUNNING) { + ifp->if_flags &= ~(IFF_UP | IFF_RUNNING); + sc->se_flags &= ~(IFF_UP | IFF_RUNNING); + } + sc->rstrtcnt++; + + if (se_verbose) + printf("se%d: %d restarts\n", ifp->if_unit, sc->rstrtcnt); + + /* + * free up any buffers currently in use + */ + for (i = 0; i < NXMT; i++) + if (sc->tpkt[i]) { + iodone(sc->tpkt[i]); + sc->tpkt[i] = (io_req_t) 0; + } + /* + * INIT the chip again, no need to reload init block address. + */ + se_ship_init_block(sc, ifp->if_unit); + + return (0); +} + +/* + * Initialize the interface. + */ +private void +se_init( int unit ) +{ + register se_softc_t sc = se_softc[unit]; + register se_desc_t *rp; + register struct ifnet *ifp = &sc->is_if; + se_reg_t rdp; + short mode; + spl_t s; + int i; + + if (ifp->if_flags & IFF_RUNNING) + return; + + rdp = sc->lnregs; + + /* + * Init the buffer descriptors and indexes for each of the rings. + */ + for (i = 0, rp = sc->lnrring; i < NRCV; i++, rp++) + se_desc_set_status(*rp, LN_RSTATE_OWN); + + for (i = 0, rp = sc->lntring; i < NXMT; i++, rp++) + se_desc_set_status(*rp, 0); + + sc->xmt_count = sc->xmt_complete = sc->xmt_last = sc->rcv_last = 0; + + /* + * Deal with loopback mode operation + */ + s = splimp(); + + (se_sw->desc_copyin) (Hmem(sc->lninit_block), (vm_offset_t)&mode, sizeof(mode)); + + if (ifp->if_flags & IFF_LOOPBACK + && ((mode & LN_MODE_LOOP) == 0)) { + /* if not already in loopback mode, do external loopback */ + mode &= ~LN_MODE_INTL; + mode |= LN_MODE_LOOP; + (se_sw->desc_copyout) ((vm_offset_t)&mode, Hmem(sc->lninit_block), sizeof(mode)); + se_restart(ifp); + se_init(ifp->if_unit); + splx(s); + return; + } + + ifp->if_flags |= (IFF_UP | IFF_RUNNING); + sc->se_flags |= (IFF_UP | IFF_RUNNING); + + /* + * Start the Lance and enable interrupts + */ + *rdp = (LN_CSR0_STRT | LN_CSR0_INEA); + wbflush(); + + /* + * See if anything is already queued + */ + se_start(unit); + splx(s); +} + + +/* + * Shut off the lance + */ +void +se_stop(int unit) +{ + se_reg_t rdp = se_softc[unit]->lnregs; + + se_write_reg(rdp, LN_CSR0_STOP, LN_CSR0_STOP, "csr0"); +} + + +/* + * Open the device, declaring the interface up + * and enabling lance interrupts. + */ +/*ARGSUSED*/ +int +se_open( + int unit, + int flag) +{ + register se_softc_t sc = se_softc[unit]; + + if (unit >= NLN) + return EINVAL; + if (!se_open_state) + return ENXIO; + + sc->is_if.if_flags |= IFF_UP; + se_open_state++; + se_init(unit); + return (0); +} + +#ifdef MAPPED +int se_use_mapped_interface[NLN]; +#endif /*MAPPED*/ + +void +se_normal(int unit) +{ +#ifdef MAPPED + se_use_mapped_interface[unit] = 0; +#endif /*MAPPED*/ + if (se_softc[unit]) { + se_restart((struct ifnet *)se_softc[unit]); + se_init(unit); + } +} + +/* + * Ethernet interface interrupt routine + */ +void +se_intr( + int unit, + spl_t spllevel) +{ + register se_softc_t sc = se_softc[unit]; + se_reg_t rdp; + register struct ifnet *ifp = &sc->is_if; + register unsigned short csr; + +#ifdef MAPPED + if (se_use_mapped_interface[unit]) + { + SE_intr(unit,spllevel); + return; + } +#endif /*MAPPED*/ + + if (se_open_state < 2) { /* Stray, or not open for business */ + rdp = (sc ? sc->lnregs : (se_reg_t)se_unprobed_addr); + *rdp |= LN_CSR0_STOP; + wbflush(); + return; + } + rdp = sc->lnregs; + + /* + * Read the CSR and process any error condition. + * Later on, restart the lance by writing back + * the CSR (for set-to-clear bits). + */ + csr = *rdp; /* pick up the csr */ + + /* drop spurious interrupts */ + if ((csr & LN_CSR0_INTR) == 0) + return; + +#ifdef DECSTATION + splx(spllevel); /* drop priority now */ +#endif /*DECSTATION*/ +again: + /* + * Check for errors first + */ + if ( csr & LN_CSR0_ERR ) { + if (csr & LN_CSR0_MISS) { + /* + * Stop the chip to prevent a corrupt packet from + * being transmitted. There is a known problem with + * missed packet errors causing corrupted data to + * be transmitted to the same host as was just + * transmitted, with a valid crc appended to the + * packet. The only solution is to stop the chip, + * which will clear the Lance silo, thus preventing + * the corrupt data from being sent. + */ + se_write_reg(rdp, LN_CSR0_STOP, LN_CSR0_STOP, "csr0"); + + sc->misscnt++; + if (se_verbose) { + int me = 0, lance = 0, index; + struct se_desc r; + for (index = 0; index < NRCV; index++) { + (se_sw->desc_copyin)( + (vm_offset_t)sc->lnrring[index], + (vm_offset_t)&r, + sizeof(r)); + if (r.status & LN_RSTATE_OWN) + lance++; + else + me++; + } + printf("se%d: missed packet (%d) csr = %x, Lance %x, me %x\n", + unit, sc->misscnt, csr, lance, me); + } + se_restart(ifp); + se_init(unit); + return; + } + if (csr & LN_CSR0_BABL) { + sc->bablcnt++; + if (se_verbose) + printf("se%d: xmt timeout (%d)\n", + unit, sc->bablcnt); + } + if (csr & LN_CSR0_MERR) { + sc->merrcnt++; + printf("se%d: memory error (%d)\n", + unit, sc->merrcnt); + + if (((csr & LN_CSR0_RXON) == 0) + || ((csr & LN_CSR0_TXON) == 0)) { + se_restart(ifp); + se_init(unit); + return; + } + } + } + + *rdp = LN_CSR0_INEA | (csr & LN_CSR0_WTC); + wbflush(); + + if ( csr & LN_CSR0_RINT ) + se_rint( unit ); + + if ( csr & LN_CSR0_TINT ) + se_tint( unit ); + + if ((csr = *rdp) & (LN_CSR0_RINT | LN_CSR0_TINT)) + goto again; +} + +/* + * Handle a transmitter complete interrupt. + */ +void +se_tint(int unit) +{ + register se_softc_t sc = se_softc[unit]; + register index; + register status; + io_req_t request; + struct se_desc r; + + /* + * Free up descriptors for all packets in queue for which + * transmission is complete. Start from queue tail, stop at first + * descriptor we do not OWN, or which is in an inconsistent state + * (lance still working). + */ + + while ((sc->xmt_complete != sc->xmt_last) && (sc->xmt_count > 0)) { + + index = sc->xmt_complete; + (se_sw->desc_copyin) ((vm_offset_t)sc->lntring[index], + (vm_offset_t)&r, sizeof(r)); + status = r.status; + + /* + * Does lance still own it ? + */ + if (status & LN_TSTATE_OWN) + break; + + /* + * Packet sent allright, release queue slot. + */ + request = sc->tpkt[index]; + sc->tpkt[index] = (io_req_t) 0; + sc->xmt_complete = ++index & (NXMT - 1); + --sc->xmt_count; + + sc->is_if.if_opackets++; + if (status & (LN_TSTATE_DEF|LN_TSTATE_ONE|LN_TSTATE_MORE)) + sc->is_if.if_collisions++; + + /* + * Check for transmission errors. + */ + if (!se_loopback_hack && status & LN_TSTATE_ERR) { + sc->is_if.if_oerrors++; + if (se_verbose) + printf("se%d: xmt error (x%x)\n", unit, r.status2); + + if (r.status2 & (LN_TSTATE2_RTRY|LN_TSTATE2_LCOL)) + sc->is_if.if_collisions++; + + /* + * Restart chip on errors that disable the + * transmitter. + */ + iodone(request); + if (r.status2 & LN_TSTATE2_DISABLE) { + register struct ifnet *ifp = &sc->is_if; + se_restart(ifp); + se_init(ifp->if_unit); + return; + } + } else if (request) { + /* + * If this was a broadcast packet loop it back. + * Signal successful transmission of the packet. + */ + register struct ether_header *eh; + register int i; + + eh = (struct ether_header *) request->io_data; + /* ether broadcast address is in the spec */ + for (i = 0; (i < 6) && (eh->ether_dhost[i] == 0xff); i++) + ; /* nop */ + /* sending to ourselves makes sense sometimes */ + if (i != 6 && se_loopback_hack) + for (i = 0; + (i < 6) && (eh->ether_dhost[i] == sc->is_addr[i]); + i++) + ; /* nop */ + if (i == 6) + se_read(sc, 0, request->io_count, request); + iodone(request); + } + } + /* + * Dequeue next transmit request, if any. + */ + if (sc->xmt_count <= 0) + se_start(unit); +} + +/* + * Handle a receiver complete interrupt. + */ +void +se_rint(int unit) +{ + register se_softc_t sc = se_softc[unit]; + register index, first, len; + unsigned char status, status1; + int ring_cnt; + struct se_desc r; + + /* + * Starting from where we left off, look around the receive ring and + * pass on all complete packets. + */ + + for (;; sc->rcv_last = ++index & (NRCV - 1)) { + + /* + * Read in current descriptor + */ +read_descriptor: + (se_sw->desc_copyin) ((vm_offset_t)sc->lnrring[sc->rcv_last], + (vm_offset_t)&r, sizeof(r)); + status = r.status; + if (status & LN_RSTATE_OWN) + break; + first = index = sc->rcv_last; + + /* + * If not the start of a packet, error + */ + if (!(status & LN_RSTATE_STP)) { + if (se_verbose) + printf("se%d: Rring #%d, status=%x !STP\n", + unit, index, status); + break; + } + /* + * See if packet is chained (should not) by looking at + * the last descriptor (OWN clear and ENP set). + * Remember the status info in this last descriptor. + */ + ring_cnt = 1, status1 = status; + while (((status1 & (LN_RSTATE_ERR | LN_RSTATE_OWN | LN_RSTATE_ENP)) == 0) && + (ring_cnt++ <= NRCV)) { + struct se_desc r1; + index = (index + 1) & (NRCV - 1); + (se_sw->desc_copyin) ((vm_offset_t)sc->lnrring[index], + (vm_offset_t)&r1, sizeof(r1)); + status1 = r1.status; + } + + /* + * Chained packet (--> illegally sized!); re-init the + * descriptors involved and ignore this bogus packet. I + * donno how, but it really happens that we get these + * monsters. + */ + if (ring_cnt > 1) { + /* + * Return all descriptors to lance + */ + se_desc_set_status(sc->lnrring[first], LN_RSTATE_OWN); + while (first != index) { + first = (first + 1) & (NRCV - 1); + se_desc_set_status(sc->lnrring[first], LN_RSTATE_OWN); + } + if ((status1 & LN_RSTATE_ERR) && se_verbose) + printf("se%d: rcv error %x (chained)\n", unit, status1); + continue; + } + + /* + * Good packets must be owned by us and have the end of + * packet flag. And nothing else. + */ + if ((status & ~LN_RSTATE_STP) == LN_RSTATE_ENP) { + sc->is_if.if_ipackets++; + + if ((len = r.message_size) == 0) + /* race seen on pmaxen: the lance + * has not updated the size yet ?? + */ + goto read_descriptor; + /* + * Drop trailing CRC bytes from len and ship packet + * up + */ + se_read(sc, (volatile char*)sc->lnrbuf[first], len-4,0); + + /* + * Return descriptor to lance, and move on to next + * packet + */ + r.status = LN_RSTATE_OWN; + (se_sw->desc_copyout)((vm_offset_t)&r, + (vm_offset_t)sc->lnrring[first], + sizeof(r)); + continue; + } + /* + * Not a good packet, see what is wrong + */ + if (status & LN_RSTATE_ERR) { + sc->is_if.if_ierrors++; + + if (se_verbose) + printf("se%d: rcv error (x%x)\n", unit, status); + + /* + * Return descriptor to lance + */ + se_desc_set_status(sc->lnrring[first], LN_RSTATE_OWN); + } else { + /* + * Race condition viz lance, Wait for the next + * interrupt. + */ + return; + } + } +} + +/* + * Output routine. + * Call common function for wiring memory, + * come back later (to se_start) to get + * things going. + */ +io_return_t +se_output( + int dev, + io_req_t ior) +{ + return net_write(&se_softc[dev]->is_if, (int(*)())se_start, ior); +} + +/* + * Start output on interface. + * + */ +void +se_start(int unit) +{ + register se_softc_t sc = se_softc[unit]; + io_req_t request; + struct se_desc r; + int tlen; + spl_t s; + register int index; + + s = splimp(); + + for (index = sc->xmt_last; + sc->xmt_count < (NXMT - 1); + sc->xmt_last = index = (index + 1) & (NXMT - 1)) { + /* + * Dequeue the next transmit request, if any. + */ + IF_DEQUEUE(&sc->is_if.if_snd, request); + if (request == 0) { + /* + * Tell the lance to send the packet now + * instead of waiting until the next 1.6 ms + * poll interval expires. + */ + *sc->lnregs = LN_CSR0_TDMD | LN_CSR0_INEA; + splx(s); + return; /* Nothing on the queue */ + } + + /* + * Keep request around until transmission complete + */ + sc->tpkt[index] = request; + tlen = copy_to_lance(request, sc->lntbuf[index]); + + /* + * Give away buffer. Must copyin/out, set len, + * and set the OWN flag. We do not do chaining. + */ + (se_sw->desc_copyin)((vm_offset_t)sc->lntring[index], + (vm_offset_t)&r, sizeof(r)); + r.buffer_size = -(tlen) | 0xf000; + r.status = (LN_TSTATE_OWN | LN_TSTATE_STP | LN_TSTATE_ENP); + (se_sw->desc_copyout)((vm_offset_t)&r, + (vm_offset_t)sc->lntring[index], + sizeof(r)); + wbflush(); + + sc->xmt_count++; + } + /* + * Since we actually have queued new packets, tell + * the chip to rescan the descriptors _now_. + * It is quite unlikely that the ring be filled, + * but if it is .. the more reason to do it! + */ + *sc->lnregs = LN_CSR0_TDMD | LN_CSR0_INEA; + splx(s); +} + + +/* + * Pull a packet off the interface and + * hand it up to the higher levels. + * + * Simulate broadcast packets in software. + */ +void +se_read( + register se_softc_t sc, + volatile char *lnrbuf, + int len, + io_req_t loop_back) +{ + register struct ifnet *ifp = &sc->is_if; + register ipc_kmsg_t new_kmsg; + char *hdr, *pkt; + + if (len <= sizeof(struct ether_header)) + return; /* sanity */ + + /* + * Get a new kmsg to put data into. + */ + new_kmsg = net_kmsg_get(); + if (new_kmsg == IKM_NULL) { + /* + * No room, drop the packet + */ + ifp->if_rcvdrops++; + return; + } + + hdr = net_kmsg(new_kmsg)->header; + pkt = net_kmsg(new_kmsg)->packet; + +#define OFF0 (sizeof(struct ether_header) - sizeof(struct packet_header)) +#define OFF1 (OFF0 & ~3) + if (loop_back) { + bcopy(loop_back->io_data, hdr, sizeof(struct ether_header)); + bcopy(loop_back->io_data + OFF0, + pkt, len - OFF0); + } else + copy_from_lance(lnrbuf, len, (struct ether_header*)hdr, + (struct packet_header*)pkt); + + /* + * Set up the 'fake' header with length. Type has been left + * in the correct place. + */ + len = len - OFF0; + ((struct packet_header *)pkt)->length = len; + + /* + * Hand the packet to the network module. + */ + net_packet(ifp, new_kmsg, len, ethernet_priority(new_kmsg)); +} + + +/* + * Get a packet out of Lance memory and into main memory. + */ +private void +copy_from_lance( + register volatile unsigned char *rbuf, + register unsigned int nbytes, + struct ether_header *hdr, + struct packet_header *pkt) +{ + /* + * Read in ethernet header + */ + (se_sw->data_copyin) ((vm_offset_t)rbuf, (vm_offset_t)hdr, sizeof(struct ether_header)); + + nbytes -= sizeof(struct ether_header); + rbuf += (se_sw->mapoffs) (sizeof(struct ether_header)); + + pkt->type = (unsigned short) hdr->ether_type; + + (se_sw->data_copyin) ((vm_offset_t)rbuf, (vm_offset_t)(pkt + 1), nbytes); +} + + +/* + * Move a packet into Lance space + */ +private int +copy_to_lance( + register io_req_t request, + volatile char *sbuf) +{ + register unsigned short *dp; + register int len; + + dp = (unsigned short *) request->io_data; + len = request->io_count; + + if (len > (int)(ETHERMTU + sizeof(struct ether_header))) { + printf("se: truncating HUGE packet\n"); + len = ETHERMTU + sizeof(struct ether_header); + } + + (se_sw->data_copyout) ((vm_offset_t)dp, (vm_offset_t)sbuf, len); + + if (len < LN_MINBUF_NOCH) + /* + * The lance needs at least this much data in a packet. Who + * cares if I send some garbage that was left in the lance + * buffer ? If one can spoof packets then one can spoof + * packets! + */ + len = LN_MINBUF_NOCH; + return len; +} + +/* + * Reset a descriptor's flags. + * Optionally give the descriptor to the lance + */ +private void +se_desc_set_status ( + register se_desc_t lndesc, + int val) +{ + struct se_desc desc; + + (se_sw->desc_copyin) ((vm_offset_t)lndesc, (vm_offset_t)&desc, sizeof(desc)); + desc.desc4.bits = 0; + desc.status = val; + (se_sw->desc_copyout) ((vm_offset_t)&desc, (vm_offset_t)lndesc, sizeof(desc)); + wbflush(); +} + +/* + * Set/Get status functions + */ +int +se_get_status( + int dev, + dev_flavor_t flavor, + dev_status_t status, /* pointer to OUT array */ + natural_t *status_count) /* out */ +{ + return (net_getstat(&se_softc[dev]->is_if, + flavor, status, status_count)); +} + +int +se_set_status( + int unit, + dev_flavor_t flavor, + dev_status_t status, + natural_t status_count) +{ + register se_softc_t sc; + + sc = se_softc[unit]; + + + switch (flavor) { + + case NET_STATUS: + break; + + case NET_ADDRESS: { + + register union ether_cvt { + unsigned char addr[6]; + int lwd[2]; + } *ec = (union ether_cvt *) status; + + if (status_count < sizeof(*ec) / sizeof(int)) + return (D_INVALID_SIZE); + + ec->lwd[0] = ntohl(ec->lwd[0]); + ec->lwd[1] = ntohl(ec->lwd[1]); + + se_setaddr(ec->addr, unit); + + break; + } + + default: + return (D_INVALID_OPERATION); + } + + return (D_SUCCESS); +} + + +/* + * Install new filter. + * Nothing special needs to be done here. + */ +io_return_t +se_setinput( + int dev, + ipc_port_t receive_port, + int priority, + filter_t *filter, + natural_t filter_count) +{ + return (net_set_filter(&se_softc[dev]->is_if, + receive_port, priority, + filter, filter_count)); +} + +/* + * Allocate and initialize a ring descriptor. + * Allocates a buffer from the lance memory and writes a descriptor + * for that buffer to the host virtual address LNDESC. + */ +private volatile long +*se_desc_alloc ( + register se_softc_t sc, + register se_desc_t lndesc) +{ + register vm_offset_t dp; /* data pointer */ + struct se_desc desc; + + /* + * Allocate buffer in lance space + */ + dp = se_malloc(sc, LN_BUFFER_SIZE); + + /* + * Build a descriptor pointing to it + */ + desc.addr_low = Addr_lo(Lmem(dp)); + desc.addr_hi = Addr_hi(Lmem(dp)); + desc.status = 0; + desc.buffer_size = -LN_BUFFER_SIZE; + desc.desc4.bits = 0; + + /* + * Copy the descriptor to lance space + */ + (se_sw->desc_copyout) ((vm_offset_t)&desc, (vm_offset_t)lndesc, sizeof(desc)); + wbflush(); + + return (volatile long *) Hmem(dp); +} + +/* + * Allocate a chunk of lance RAM buffer. Since we never + * give lance RAM buffer memory back, we'll just step up the + * byte-count on a per-unit basis. + * + * The return value is an index into the lance memory, which can be + * passed with Hmem() and Lmem() to get the host and chip virtual addresses. + */ +private vm_offset_t +se_malloc( + se_softc_t sc, + int size) +{ + register vm_offset_t ret; + + /* + * On first call, zero lance memory + */ + if (sc->lnsbrk == 0) + (se_sw->bzero) (Hmem(0), LN_MEMORY_SIZE); + + /* + * Start out on the first double longword boundary + * (this accomodates some machines, with minimal loss) + */ + if (sc->lnsbrk & 0xf) + sc->lnsbrk = (sc->lnsbrk + 0x10) & ~0xf; + + ret = sc->lnsbrk; + sc->lnsbrk += size; + + if (sc->lnsbrk > LN_MEMORY_SIZE) + panic("se_malloc"); + + return ret; +} + +#endif NLN > 0 |