From a8bf95d0249565f4210ccab5c13232d501ce0c2d Mon Sep 17 00:00:00 2001 From: Till Straumann Date: Wed, 22 Apr 2009 22:06:58 +0000 Subject: - importing updated version from SLAC as of 20090422 --- bsd_eth_drivers/if_le/if_le_lebuffer.c | 408 +++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 bsd_eth_drivers/if_le/if_le_lebuffer.c (limited to 'bsd_eth_drivers/if_le/if_le_lebuffer.c') diff --git a/bsd_eth_drivers/if_le/if_le_lebuffer.c b/bsd_eth_drivers/if_le/if_le_lebuffer.c new file mode 100644 index 0000000..5ad8d31 --- /dev/null +++ b/bsd_eth_drivers/if_le/if_le_lebuffer.c @@ -0,0 +1,408 @@ +/*- + * Copyright (c) 2006 Marius Strobl + * 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 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 AUTHOR 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 +__FBSDID("$FreeBSD: src/sys/dev/le/if_le_lebuffer.c,v 1.2 2007/02/23 12:18:45 piso Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +/* + * LANCE registers + */ +#define LEREG1_RDP 0 /* Register Data port */ +#define LEREG1_RAP 2 /* Register Address port */ + +struct le_lebuffer_softc { + struct am7990_softc sc_am7990; /* glue to MI code */ + + int sc_brid; + struct resource *sc_bres; + bus_space_tag_t sc_buft; + bus_space_handle_t sc_bufh; + + int sc_rrid; + struct resource *sc_rres; + bus_space_tag_t sc_regt; + bus_space_handle_t sc_regh; + + int sc_irid; + struct resource *sc_ires; + void *sc_ih; +}; + +static devclass_t le_lebuffer_devclass; + +static device_probe_t le_lebuffer_probe; +static device_attach_t le_lebuffer_attach; +static device_detach_t le_lebuffer_detach; +static device_resume_t le_buffer_resume; +static device_suspend_t le_buffer_suspend; + +static device_method_t le_lebuffer_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, le_lebuffer_probe), + DEVMETHOD(device_attach, le_lebuffer_attach), + DEVMETHOD(device_detach, le_lebuffer_detach), + /* We can just use the suspend method here. */ + DEVMETHOD(device_shutdown, le_buffer_suspend), + DEVMETHOD(device_suspend, le_buffer_suspend), + DEVMETHOD(device_resume, le_buffer_resume), + + { 0, 0 } +}; + +DEFINE_CLASS_0(le, le_lebuffer_driver, le_lebuffer_methods, + sizeof(struct le_lebuffer_softc)); +DRIVER_MODULE(le, lebuffer, le_lebuffer_driver, le_lebuffer_devclass, 0, 0); +MODULE_DEPEND(le, ether, 1, 1, 1); + +/* + * Media types supported + */ +static const int le_lebuffer_media[] = { + IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0) +}; +#define NLEMEDIA \ + (sizeof(le_lebuffer_media) / sizeof(le_lebuffer_media[0])) + +static void le_lebuffer_wrcsr(struct lance_softc *, uint16_t, uint16_t); +static uint16_t le_lebuffer_rdcsr(struct lance_softc *, uint16_t); +static void le_lebuffer_copytodesc(struct lance_softc *, void *, int, int); +static void le_lebuffer_copyfromdesc(struct lance_softc *, void *, int, int); +static void le_lebuffer_copytobuf(struct lance_softc *, void *, int, int); +static void le_lebuffer_copyfrombuf(struct lance_softc *, void *, int, int); +static void le_lebuffer_zerobuf(struct lance_softc *, int, int); + +static void +le_lebuffer_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val) +{ + struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; + + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, port); + bus_space_barrier(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, 2, + BUS_SPACE_BARRIER_WRITE); + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RDP, val); +} + +static uint16_t +le_lebuffer_rdcsr(struct lance_softc *sc, uint16_t port) +{ + struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; + + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, port); + bus_space_barrier(lesc->sc_regt, lesc->sc_regh, LEREG1_RAP, 2, + BUS_SPACE_BARRIER_WRITE); + return (bus_space_read_2(lesc->sc_regt, lesc->sc_regh, LEREG1_RDP)); +} + +/* + * It turns out that using bus_space(9) to access the buffers and the + * descriptors yields way more throughput than accessing them via the + * KVA returned by rman_get_virtual(9). The descriptor rings can be + * accessed using 8-bit up to 64-bit operations while the buffers can + * be only accessed using 8-bit and 16-bit operations. + * NB: For whatever reason setting LE_C3_BSWP has no effect with at + * least the 501-2981 (although their 'busmaster-regval' property + * indicates to set LE_C3_BSWP also for these cards), so we need + * to manually byte swap access to the buffers, i.e. the accesses + * going through the RX/TX FIFOs. + */ + +static void +le_lebuffer_copytodesc(struct lance_softc *sc, void *fromv, int off, int len) +{ + struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; + caddr_t from = fromv; + + for (; len >= 8; len -= 8, off += 8, from += 8) + bus_space_write_8(lesc->sc_buft, lesc->sc_bufh, off, + be64dec(from)); + for (; len >= 4; len -= 4, off += 4, from += 4) + bus_space_write_4(lesc->sc_buft, lesc->sc_bufh, off, + be32dec(from)); + for (; len >= 2; len -= 2, off += 2, from += 2) + bus_space_write_2(lesc->sc_buft, lesc->sc_bufh, off, + be16dec(from)); + if (len == 1) + bus_space_write_1(lesc->sc_buft, lesc->sc_bufh, off, + *from); +} + +static void +le_lebuffer_copyfromdesc(struct lance_softc *sc, void *tov, int off, int len) +{ + struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; + caddr_t to = tov; + + for (; len >= 8; len -= 8, off += 8, to += 8) + be64enc(to, + bus_space_read_8(lesc->sc_buft, lesc->sc_bufh, off)); + for (; len >= 4; len -= 4, off += 4, to += 4) + be32enc(to, + bus_space_read_4(lesc->sc_buft, lesc->sc_bufh, off)); + for (; len >= 2; len -= 2, off += 2, to += 2) + be16enc(to, + bus_space_read_2(lesc->sc_buft, lesc->sc_bufh, off)); + if (len == 1) + *to = + bus_space_read_1(lesc->sc_buft, lesc->sc_bufh, off); +} + +static void +le_lebuffer_copytobuf(struct lance_softc *sc, void *fromv, int off, int len) +{ + struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; + caddr_t from = fromv; + + for (; len >= 2; len -= 2, off += 2, from += 2) + bus_space_write_2(lesc->sc_buft, lesc->sc_bufh, off, + le16dec(from)); + if (len == 1) + bus_space_write_1(lesc->sc_buft, lesc->sc_bufh, off + 1, + *from); +} + +static void +le_lebuffer_copyfrombuf(struct lance_softc *sc, void *tov, int off, int len) +{ + struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; + caddr_t to = tov; + + for (; len >= 2; len -= 2, off += 2, to += 2) + le16enc(to, + bus_space_read_2(lesc->sc_buft, lesc->sc_bufh, off)); + if (len == 1) + *to = + bus_space_read_1(lesc->sc_buft, lesc->sc_bufh, off + 1); +} + +static void +le_lebuffer_zerobuf(struct lance_softc *sc, int off, int len) +{ + struct le_lebuffer_softc *lesc = (struct le_lebuffer_softc *)sc; + + for (; len >= 2; len -= 2, off += 2) + bus_space_write_2(lesc->sc_buft, lesc->sc_bufh, off, 0); + if (len == 1) + bus_space_write_1(lesc->sc_buft, lesc->sc_bufh, off + 1, 0); +} + +static int +le_lebuffer_probe(device_t dev) +{ + + if (strcmp(ofw_bus_get_name(dev), "le") == 0) { + device_set_desc(dev, "LANCE Ethernet"); + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +static int +le_lebuffer_attach(device_t dev) +{ + struct le_lebuffer_softc *lesc; + struct lance_softc *sc; + int error; + + lesc = device_get_softc(dev); + sc = &lesc->sc_am7990.lsc; + + LE_LOCK_INIT(sc, device_get_nameunit(dev)); + + /* + * The "register space" of the parent is just a buffer where the + * the LANCE descriptor rings and the RX/TX buffers can be stored. + */ + lesc->sc_brid = 0; + lesc->sc_bres = bus_alloc_resource_any(device_get_parent(dev), + SYS_RES_MEMORY, &lesc->sc_brid, RF_ACTIVE); + if (lesc->sc_bres == NULL) { + device_printf(dev, "cannot allocate LANCE buffer\n"); + error = ENXIO; + goto fail_mtx; + } + lesc->sc_buft = rman_get_bustag(lesc->sc_bres); + lesc->sc_bufh = rman_get_bushandle(lesc->sc_bres); + + /* Allocate LANCE registers. */ + lesc->sc_rrid = 0; + lesc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &lesc->sc_rrid, RF_ACTIVE); + if (lesc->sc_rres == NULL) { + device_printf(dev, "cannot allocate LANCE registers\n"); + error = ENXIO; + goto fail_bres; + } + lesc->sc_regt = rman_get_bustag(lesc->sc_rres); + lesc->sc_regh = rman_get_bushandle(lesc->sc_rres); + + /* Allocate LANCE interrupt. */ + lesc->sc_irid = 0; + if ((lesc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &lesc->sc_irid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { + device_printf(dev, "cannot allocate interrupt\n"); + error = ENXIO; + goto fail_rres; + } + + /* + * LANCE view is offset by buffer location. + * Note that we don't use sc->sc_mem. + */ + sc->sc_addr = 0; + sc->sc_memsize = rman_get_size(lesc->sc_bres); + sc->sc_flags = 0; + + /* That old black magic... */ + if (OF_getprop(ofw_bus_get_node(dev), "busmaster-regval", + &sc->sc_conf3, sizeof(sc->sc_conf3)) == -1) + sc->sc_conf3 = LE_C3_ACON | LE_C3_BCON; + /* + * Make sure LE_C3_BSWP is cleared so that for cards where + * that flag actually works le_lebuffer_copy{from,to}buf() + * don't fail... + */ + sc->sc_conf3 &= ~LE_C3_BSWP; + + OF_getetheraddr(dev, sc->sc_enaddr); + + sc->sc_copytodesc = le_lebuffer_copytodesc; + sc->sc_copyfromdesc = le_lebuffer_copyfromdesc; + sc->sc_copytobuf = le_lebuffer_copytobuf; + sc->sc_copyfrombuf = le_lebuffer_copyfrombuf; + sc->sc_zerobuf = le_lebuffer_zerobuf; + + sc->sc_rdcsr = le_lebuffer_rdcsr; + sc->sc_wrcsr = le_lebuffer_wrcsr; + sc->sc_hwreset = NULL; + sc->sc_hwinit = NULL; + sc->sc_hwintr = NULL; + sc->sc_nocarrier = NULL; + sc->sc_mediachange = NULL; + sc->sc_mediastatus = NULL; + sc->sc_supmedia = le_lebuffer_media; + sc->sc_nsupmedia = NLEMEDIA; + sc->sc_defaultmedia = le_lebuffer_media[0]; + + error = am7990_config(&lesc->sc_am7990, device_get_name(dev), + device_get_unit(dev)); + if (error != 0) { + device_printf(dev, "cannot attach Am7990\n"); + goto fail_ires; + } + + error = bus_setup_intr(dev, lesc->sc_ires, INTR_TYPE_NET | INTR_MPSAFE, + NULL, am7990_intr, sc, &lesc->sc_ih); + if (error != 0) { + device_printf(dev, "cannot set up interrupt\n"); + goto fail_am7990; + } + + return (0); + + fail_am7990: + am7990_detach(&lesc->sc_am7990); + fail_ires: + bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires); + fail_rres: + bus_release_resource(dev, SYS_RES_MEMORY, lesc->sc_rrid, lesc->sc_rres); + fail_bres: + bus_release_resource(device_get_parent(dev), SYS_RES_MEMORY, + lesc->sc_brid, lesc->sc_bres); + fail_mtx: + LE_LOCK_DESTROY(sc); + return (error); +} + +static int +le_lebuffer_detach(device_t dev) +{ + struct le_lebuffer_softc *lesc; + struct lance_softc *sc; + + lesc = device_get_softc(dev); + sc = &lesc->sc_am7990.lsc; + + bus_teardown_intr(dev, lesc->sc_ires, lesc->sc_ih); + am7990_detach(&lesc->sc_am7990); + bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires); + bus_release_resource(dev, SYS_RES_MEMORY, lesc->sc_rrid, lesc->sc_rres); + bus_release_resource(device_get_parent(dev), SYS_RES_MEMORY, + lesc->sc_brid, lesc->sc_bres); + LE_LOCK_DESTROY(sc); + + return (0); +} + +static int +le_buffer_suspend(device_t dev) +{ + struct le_lebuffer_softc *lesc; + + lesc = device_get_softc(dev); + + lance_suspend(&lesc->sc_am7990.lsc); + + return (0); +} + +static int +le_buffer_resume(device_t dev) +{ + struct le_lebuffer_softc *lesc; + + lesc = device_get_softc(dev); + + lance_resume(&lesc->sc_am7990.lsc); + + return (0); +} -- cgit v1.2.3