diff options
Diffstat (limited to 'sys/arch/mvme68k/stand/netboot/if_le.c')
-rw-r--r-- | sys/arch/mvme68k/stand/netboot/if_le.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/sys/arch/mvme68k/stand/netboot/if_le.c b/sys/arch/mvme68k/stand/netboot/if_le.c new file mode 100644 index 00000000000..8049b69b9e0 --- /dev/null +++ b/sys/arch/mvme68k/stand/netboot/if_le.c @@ -0,0 +1,431 @@ +/* $NetBSD: le_poll.c,v 1.3 1994/10/26 09:11:48 cgd Exp $ */ + +/* + * Copyright (c) 1993 Adam Glass + * 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, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Glass. + * 4. The name of the Author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Adam Glass ``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 REGENTS 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/param.h> +#include <sys/types.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> + +#include "stand.h" +#include "netif.h" +#include "config.h" + +#include "if_lereg.h" + +int le_debug = 0; + +void le_end __P((struct netif *)); +void le_error __P((struct netif *, char *, volatile struct lereg1 *)); +int le_get __P((struct iodesc *, void *, int, time_t)); +void le_init __P((struct iodesc *, void *)); +int le_match __P((struct netif *, void *)); +int le_poll __P((struct iodesc *, void *, int)); +int le_probe __P((struct netif *, void *)); +int le_put __P((struct iodesc *, void *, int)); +void le_reset __P((struct netif *, u_char *)); + +struct netif_stats le_stats; + +struct netif_dif le0_dif = { + 0, /* unit */ + 1, /* nsel */ + &le_stats, + 0, + 0, +}; + +struct netif_driver le_driver = { + "le", /* netif_bname */ + le_match, /* match */ + le_probe, /* probe */ + le_init, /* init */ + le_get, /* get */ + le_put, /* put */ + le_end, /* end */ + &le0_dif, /* netif_ifs */ + 1, /* netif_nifs */ +}; + +struct le_configuration { + unsigned int phys_addr; + int used; +} le_config[] = { + { LANCE_REG_ADDR, 0 } +}; + +int nle_config = sizeof(le_config) / (sizeof(le_config[0])); + +struct { + struct lereg1 *sc_r1; /* LANCE registers */ + struct lereg2 *sc_r2; /* RAM */ + int next_rmd; + int next_tmd; +} le_softc; + +int +le_match(nif, machdep_hint) + struct netif *nif; + void *machdep_hint; +{ + char *name; + int i, val = 0; + extern int cputyp; + + if (cputyp != CPU_147) + return (0); + name = machdep_hint; + if (name && !bcmp(le_driver.netif_bname, name, 2)) + val += 10; + for (i = 0; i < nle_config; i++) { + if (le_config[i].used) + continue; + if (le_debug) + printf("le%d: le_match --> %d\n", i, val + 1); + le_config[i].used++; + return val + 1; + } + if (le_debug) + printf("le%d: le_match --> 0\n", i); + return 0; +} + +int +le_probe(nif, machdep_hint) + struct netif *nif; + void *machdep_hint; +{ + extern int cputyp; + + /* the set unit is the current unit */ + if (le_debug) + printf("le%d: le_probe called\n", nif->nif_unit); + + if (cputyp == CPU_147) + return 0; + return 1; +} + +void +le_error(nif, str, ler1) + struct netif *nif; + char *str; + volatile struct lereg1 *ler1; +{ + /* ler1->ler1_rap = LE_CSRO done in caller */ + if (ler1->ler1_rdp & LE_C0_BABL) + panic("le%d: been babbling, found by '%s'\n", nif->nif_unit, str); + if (ler1->ler1_rdp & LE_C0_CERR) { + le_stats.collision_error++; + ler1->ler1_rdp = LE_C0_CERR; + } + if (ler1->ler1_rdp & LE_C0_MISS) { + le_stats.missed++; + ler1->ler1_rdp = LE_C0_MISS; + } + if (ler1->ler1_rdp & LE_C0_MERR) { + printf("le%d: memory error in '%s'\n", nif->nif_unit, str); + panic("memory error"); + } +} + +void +le_reset(nif, myea) + struct netif *nif; + u_char *myea; +{ + struct lereg1 *ler1 = le_softc.sc_r1; + struct lereg2 *ler2 = le_softc.sc_r2; + unsigned int a; + int timo = 100000, stat, i; + + if (le_debug) + printf("le%d: le_reset called\n", nif->nif_unit); + ler1->ler1_rap = LE_CSR0; + ler1->ler1_rdp = LE_C0_STOP; /* do nothing until we are finished */ + + bzero(ler2, sizeof(*ler2)); + + ler2->ler2_mode = LE_MODE_NORMAL; + ler2->ler2_padr[0] = myea[1]; + ler2->ler2_padr[1] = myea[0]; + ler2->ler2_padr[2] = myea[3]; + ler2->ler2_padr[3] = myea[2]; + ler2->ler2_padr[4] = myea[5]; + ler2->ler2_padr[5] = myea[4]; + + + ler2->ler2_ladrf0 = 0; + ler2->ler2_ladrf1 = 0; + + a = (u_int) ler2->ler2_rmd; + ler2->ler2_rlen = LE_RLEN | (a >> 16); + ler2->ler2_rdra = a & LE_ADDR_LOW_MASK; + + a = (u_int) ler2->ler2_tmd; + ler2->ler2_tlen = LE_TLEN | (a >> 16); + ler2->ler2_tdra = a & LE_ADDR_LOW_MASK; + + ler1->ler1_rap = LE_CSR1; + a = (u_int) ler2; + ler1->ler1_rdp = a & LE_ADDR_LOW_MASK; + ler1->ler1_rap = LE_CSR2; + ler1->ler1_rdp = a >> 16; + + for (i = 0; i < LERBUF; i++) { + a = (u_int) & ler2->ler2_rbuf[i]; + ler2->ler2_rmd[i].rmd0 = a & LE_ADDR_LOW_MASK; + ler2->ler2_rmd[i].rmd1_bits = LE_R1_OWN; + ler2->ler2_rmd[i].rmd1_hadr = a >> 16; + ler2->ler2_rmd[i].rmd2 = -LEMTU; + ler2->ler2_rmd[i].rmd3 = 0; + } + for (i = 0; i < LETBUF; i++) { + a = (u_int) & ler2->ler2_tbuf[i]; + ler2->ler2_tmd[i].tmd0 = a & LE_ADDR_LOW_MASK; + ler2->ler2_tmd[i].tmd1_bits = 0; + ler2->ler2_tmd[i].tmd1_hadr = a >> 16; + ler2->ler2_tmd[i].tmd2 = 0; + ler2->ler2_tmd[i].tmd3 = 0; + } + + ler1->ler1_rap = LE_CSR3; + ler1->ler1_rdp = LE_C3_BSWP; + + ler1->ler1_rap = LE_CSR0; + ler1->ler1_rdp = LE_C0_INIT; + do { + if (--timo == 0) { + printf("le%d: init timeout, stat = 0x%x\n", + nif->nif_unit, stat); + break; + } + stat = ler1->ler1_rdp; + } while ((stat & LE_C0_IDON) == 0); + + ler1->ler1_rdp = LE_C0_IDON; + le_softc.next_rmd = 0; + le_softc.next_tmd = 0; + ler1->ler1_rap = LE_CSR0; + ler1->ler1_rdp = LE_C0_STRT; +} + +int +le_poll(desc, pkt, len) + struct iodesc *desc; + void *pkt; + int len; +{ + struct lereg1 *ler1 = le_softc.sc_r1; + struct lereg2 *ler2 = le_softc.sc_r2; + unsigned int a; + int length; + struct lermd *rmd; + + + ler1->ler1_rap = LE_CSR0; + if ((ler1->ler1_rdp & LE_C0_RINT) != 0) + ler1->ler1_rdp = LE_C0_RINT; + rmd = &ler2->ler2_rmd[le_softc.next_rmd]; + if (rmd->rmd1_bits & LE_R1_OWN) { + return (0); + } + if (ler1->ler1_rdp & LE_C0_ERR) + le_error(desc->io_netif, "le_poll", ler1); + if (rmd->rmd1_bits & LE_R1_ERR) { + printf("le%d_poll: rmd status 0x%x\n", desc->io_netif->nif_unit, + rmd->rmd1_bits); + length = 0; + goto cleanup; + } + if ((rmd->rmd1_bits & (LE_R1_STP | LE_R1_ENP)) != (LE_R1_STP | LE_R1_ENP)) + panic("le_poll: chained packet\n"); + + length = rmd->rmd3; + if (length >= LEMTU) { + length = 0; + panic("csr0 when bad things happen: %x\n", ler1->ler1_rdp); + goto cleanup; + } + if (!length) + goto cleanup; + length -= 4; + if (length > 0) { + + /* + * if buffer is smaller than the packet truncate it. + * (is this wise?) + */ + if (length > len) + length = len; + + bcopy(&ler2->ler2_rbuf[le_softc.next_rmd], pkt, length); + } +cleanup: + a = (u_int) & ler2->ler2_rbuf[le_softc.next_rmd]; + rmd->rmd0 = a & LE_ADDR_LOW_MASK; + rmd->rmd1_hadr = a >> 16; + rmd->rmd2 = -LEMTU; + le_softc.next_rmd = + (le_softc.next_rmd == (LERBUF - 1)) ? 0 : (le_softc.next_rmd + 1); + rmd->rmd1_bits = LE_R1_OWN; + return length; +} + +int +le_put(desc, pkt, len) + struct iodesc *desc; + void *pkt; + int len; +{ + volatile struct lereg1 *ler1 = le_softc.sc_r1; + volatile struct lereg2 *ler2 = le_softc.sc_r2; + volatile struct letmd *tmd; + int timo = 100000, stat, i; + unsigned int a; + + ler1->ler1_rap = LE_CSR0; + if (ler1->ler1_rdp & LE_C0_ERR) + le_error(desc->io_netif, "le_put(way before xmit)", ler1); + tmd = &ler2->ler2_tmd[le_softc.next_tmd]; + while (tmd->tmd1_bits & LE_T1_OWN) { + printf("le%d: output buffer busy\n", desc->io_netif->nif_unit); + } + bcopy(pkt, ler2->ler2_tbuf[le_softc.next_tmd], len); + if (len < 64) + tmd->tmd2 = -64; + else + tmd->tmd2 = -len; + tmd->tmd3 = 0; + if (ler1->ler1_rdp & LE_C0_ERR) + le_error(desc->io_netif, "le_put(before xmit)", ler1); + tmd->tmd1_bits = LE_T1_STP | LE_T1_ENP | LE_T1_OWN; + a = (u_int) & ler2->ler2_tbuf[le_softc.next_tmd]; + tmd->tmd0 = a & LE_ADDR_LOW_MASK; + tmd->tmd1_hadr = a >> 16; + ler1->ler1_rdp = LE_C0_TDMD; + if (ler1->ler1_rdp & LE_C0_ERR) + le_error(desc->io_netif, "le_put(after xmit)", ler1); + do { + if (--timo == 0) { + printf("le%d: transmit timeout, stat = 0x%x\n", + desc->io_netif->nif_unit, stat); + if (ler1->ler1_rdp & LE_C0_ERR) + le_error(desc->io_netif, "le_put(timeout)", ler1); + break; + } + stat = ler1->ler1_rdp; + } while ((stat & LE_C0_TINT) == 0); + ler1->ler1_rdp = LE_C0_TINT; + if (ler1->ler1_rdp & LE_C0_ERR) { + if ((ler1->ler1_rdp & (LE_C0_BABL | LE_C0_CERR | LE_C0_MISS | + LE_C0_MERR)) != + LE_C0_CERR) + printf("le_put: xmit error, buf %d\n", le_softc.next_tmd); + le_error(desc->io_netif, "le_put(xmit error)", ler1); + } + le_softc.next_tmd = 0; +/* (le_softc.next_tmd == (LETBUF - 1)) ? 0 : le_softc.next_tmd + 1;*/ + if (tmd->tmd1_bits & LE_T1_DEF) + le_stats.deferred++; + if (tmd->tmd1_bits & LE_T1_ONE) + le_stats.collisions++; + if (tmd->tmd1_bits & LE_T1_MORE) + le_stats.collisions += 2; + if (tmd->tmd1_bits & LE_T1_ERR) { + printf("le%d: transmit error, error = 0x%x\n", desc->io_netif->nif_unit, + tmd->tmd3); + return -1; + } + if (le_debug) { + printf("le%d: le_put() successful: sent %d\n", + desc->io_netif->nif_unit, len); + printf("le%d: le_put(): tmd1_bits: %x tmd3: %x\n", + desc->io_netif->nif_unit, + (unsigned int) tmd->tmd1_bits, + (unsigned int) tmd->tmd3); + } + return len; +} + +int +le_get(desc, pkt, len, timeout) + struct iodesc *desc; + void *pkt; + int len; + time_t timeout; +{ + time_t t; + int cc; + + t = getsecs(); + cc = 0; + while (((getsecs() - t) < timeout) && !cc) { + cc = le_poll(desc, pkt, len); + } + return cc; +} +/* + * init le device. return 0 on failure, 1 if ok. + */ +void +le_init(desc, machdep_hint) + struct iodesc *desc; + void *machdep_hint; +{ + u_long eram = 4*1024*1024; + struct netif *nif = desc->io_netif; + + if (le_debug) + printf("le%d: le_init called\n", desc->io_netif->nif_unit); + machdep_common_ether(desc->myea); + bzero(&le_softc, sizeof(le_softc)); + le_softc.sc_r1 = + (struct lereg1 *) le_config[desc->io_netif->nif_unit].phys_addr; + le_softc.sc_r2 = (struct lereg2 *) (eram - (1024 * 1024)); + le_reset(desc->io_netif, desc->myea); + printf("device: %s%d attached to %s\n", nif->nif_driver->netif_bname, + nif->nif_unit, ether_sprintf(desc->myea)); +} + +void +le_end(nif) + struct netif *nif; +{ + struct lereg1 *ler1 = le_softc.sc_r1; + + if (le_debug) + printf("le%d: le_end called\n", nif->nif_unit); + ler1->ler1_rap = LE_CSR0; + ler1->ler1_rdp = LE_C0_STOP; +} |