/* $NetBSD: lance.c,v 1.3 2014/04/16 11:18:00 tsutsui Exp $ */ /* * Copyright (c) 2013 Izumi Tsutsui. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. */ /*- * Copyright (c) 2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by UCHIYAMA Yasushi. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * LANCE driver for LUNA * based on sys/arch/ews4800mips/stand/common/lance.c */ #include #include #include #include #include #include static void lance_setup(struct le_softc *); static bool lance_set_initblock(struct le_softc *); static bool lance_do_initialize(struct le_softc *); #define NLE 1 /* XXX for now */ static struct le_softc lesc[NLE]; void * lance_attach(int unit, void *reg, void *mem, uint8_t *eaddr) { struct le_softc *sc; if (unit >= NLE) { printf("%s: invalid unit number\n", __func__); return NULL; } sc = &lesc[unit]; if (sc->sc_reg != NULL) { printf("%s: unit %d is already attached\n", __func__, unit); return NULL; } sc->sc_reg = reg; sc->sc_mem = mem; memcpy(sc->sc_enaddr, eaddr, 6); return sc; } void * lance_cookie(int unit) { struct le_softc *sc; if (unit >= NLE) return NULL; sc = &lesc[unit]; if (sc->sc_reg == NULL) return NULL; return sc; } uint8_t * lance_eaddr(void *cookie) { struct le_softc *sc = cookie; if (sc == NULL || sc->sc_reg == NULL) return NULL; return sc->sc_enaddr; } bool lance_init(void *cookie) { struct le_softc *sc = cookie; lance_setup(sc); if (!lance_set_initblock(sc)) return false; if (!lance_do_initialize(sc)) return false; return true; } int lance_get(void *cookie, void *data, size_t maxlen) { struct le_softc *sc = cookie; struct lereg *lereg = sc->sc_reg; struct lemem *lemem = sc->sc_mem; struct lermd_v *rmd; uint16_t csr __unused; int len = -1; lereg->ler_rap = LE_CSR0; if ((lereg->ler_rdp & LE_C0_RINT) != 0) lereg->ler_rdp = LE_C0_RINT; rmd = &lemem->lem_rmd[sc->sc_currmd]; if ((rmd->rmd1_bits & LE_R1_OWN) != 0) return -1; csr = lereg->ler_rdp; #if 0 if ((csr & LE_C0_ERR) != 0) printf("%s: RX poll error (CSR=0x%x)\n", __func__, csr); #endif if ((rmd->rmd1_bits & LE_R1_ERR) != 0) { printf("%s: RX error (rmd status=0x%x)\n", __func__, rmd->rmd1_bits); goto out; } len = rmd->rmd3; if (len < LEMINSIZE + 4 || len > LEMTU) { printf("%s: RX error (bad length %d)\n", __func__, len); goto out; } len -= 4; memcpy(data, (void *)lemem->lem_rbuf[sc->sc_currmd], min(len, maxlen)); out: rmd->rmd2 = -LEMTU; rmd->rmd1_bits = LE_R1_OWN; /* return to LANCE */ sc->sc_currmd = LE_NEXTRMD(sc->sc_currmd); return len; } bool lance_put(void *cookie, void *data, size_t len) { struct le_softc *sc = cookie; struct lereg *lereg = sc->sc_reg; struct lemem *lemem = sc->sc_mem; struct letmd_v *tmd; uint16_t stat; int timeout; lereg->ler_rap = LE_CSR0; stat = lereg->ler_rdp; lereg->ler_rdp = stat & (LE_C0_BABL | LE_C0_CERR | LE_C0_MISS | LE_C0_TINT); #if 0 if (stat & (LE_C0_BABL | LE_C0_CERR | LE_C0_MISS | LE_C0_MERR)) printf("%s: TX error before xmit csr0=0x%x\n", __func__, stat); #endif /* setup TX descriptor */ tmd = &lemem->lem_tmd[sc->sc_curtmd]; while (tmd->tmd1_bits & LE_T1_OWN) continue; tmd->tmd1_bits = LE_T1_STP | LE_T1_ENP; memcpy((void *)lemem->lem_tbuf[sc->sc_curtmd], data, len); tmd->tmd2 = -max(len, LEMINSIZE); tmd->tmd3 = 0; /* start TX */ tmd->tmd1_bits |= LE_T1_OWN; lereg->ler_rap = LE_CSR0; lereg->ler_rdp = LE_C0_TDMD; /* check TX complete */ timeout = 0; do { lereg->ler_rap = LE_CSR0; stat = lereg->ler_rdp; #if 0 if (stat & LE_C0_ERR) { printf("%s: TX error (CSR0=%x)\n", __func__, stat); if (stat & LE_C0_CERR) { lereg->ler_rdp = LE_C0_CERR; } } #endif if (timeout++ > 1000) { printf("%s: TX timeout (CSR0=%x)\n", __func__, stat); return false; } } while ((stat & LE_C0_TINT) == 0); lereg->ler_rdp = LE_C0_TINT; sc->sc_curtmd = LE_NEXTTMD(sc->sc_curtmd); return true; } bool lance_end(void *cookie) { struct le_softc *sc = cookie; struct lereg *lereg = sc->sc_reg; lereg->ler_rap = LE_CSR0; lereg->ler_rdp = LE_C0_STOP; return true; } /* XXX */ int lance_intr(void) { return 1; } static bool lance_set_initblock(struct le_softc *sc) { struct lereg *lereg = sc->sc_reg; uint32_t addr = (uint32_t)sc->sc_mem; lereg->ler_rap = LE_CSR0; lereg->ler_rdp = LE_C0_STOP; /* disable all external activity */ DELAY(100); /* Set the correct byte swapping mode */ lereg->ler_rap = LE_CSR3; lereg->ler_rdp = LE_C3_BSWP; /* Low address of init block */ lereg->ler_rap = LE_CSR1; lereg->ler_rdp = addr & 0xfffe; /* High address of init block */ lereg->ler_rap = LE_CSR2; lereg->ler_rdp = (addr >> 16) & 0x00ff; DELAY(100); return true; } static bool lance_do_initialize(struct le_softc *sc) { struct lereg *lereg = sc->sc_reg; uint16_t reg; int timeout; sc->sc_curtmd = 0; sc->sc_currmd = 0; /* Initialze LANCE */ lereg->ler_rap = LE_CSR0; lereg->ler_rdp = LE_C0_INIT; /* Wait interrupt */ timeout = 1000000; do { lereg->ler_rap = LE_CSR0; reg = lereg->ler_rdp; if (--timeout == 0) { printf("le: init timeout (CSR=0x%x)\n", reg); return false; } DELAY(1); } while ((reg & LE_C0_IDON) == 0); lereg->ler_rap = LE_CSR0; lereg->ler_rdp = LE_C0_STRT | LE_C0_IDON; return true; } static void lance_setup(struct le_softc *sc) { struct lereg *lereg = sc->sc_reg; struct lemem *lemem = sc->sc_mem; uint32_t addr; int i; /* make sure to stop LANCE chip before setup memory */ lereg->ler_rap = LE_CSR0; lereg->ler_rdp = LE_C0_STOP; memset(lemem, 0, sizeof *lemem); /* Init block */ lemem->lem_mode = LE_MODE_NORMAL; lemem->lem_padr[0] = (sc->sc_enaddr[1] << 8) | sc->sc_enaddr[0]; lemem->lem_padr[1] = (sc->sc_enaddr[3] << 8) | sc->sc_enaddr[2]; lemem->lem_padr[2] = (sc->sc_enaddr[5] << 8) | sc->sc_enaddr[4]; /* Logical address filter */ for (i = 0; i < 4; i++) lemem->lem_ladrf[i] = 0x0000; /* Location of Rx descriptor ring */ addr = (uint32_t)lemem->lem_rmd; lemem->lem_rdra = addr & 0xffff; lemem->lem_rlen = LE_RLEN | ((addr >> 16) & 0xff); /* Location of Tx descriptor ring */ addr = (uint32_t)lemem->lem_tmd; lemem->lem_tdra = addr & 0xffff; lemem->lem_tlen = LE_TLEN | ((addr >> 16) & 0xff); /* Rx descriptor */ for (i = 0; i < LERBUF; i++) { addr = (uint32_t)lemem->lem_rbuf[i]; lemem->lem_rmd[i].rmd0 = addr & 0xffff; lemem->lem_rmd[i].rmd1_hadr = (addr >> 16) & 0xff; lemem->lem_rmd[i].rmd1_bits = LE_R1_OWN; lemem->lem_rmd[i].rmd2 = LE_XMD2_ONES | -LEMTU; lemem->lem_rmd[i].rmd3 = 0; } /* Tx descriptor */ for (i = 0; i < LETBUF; i++) { addr = (uint32_t)lemem->lem_tbuf[i]; lemem->lem_tmd[i].tmd0 = addr & 0xffff; lemem->lem_tmd[i].tmd1_hadr = (addr >> 16) & 0xff; lemem->lem_tmd[i].tmd1_bits = 0; lemem->lem_tmd[i].tmd2 = LE_XMD2_ONES | 0; lemem->lem_tmd[i].tmd3 = 0; } }