diff options
Diffstat (limited to 'sys/dev/patm/if_patm_attach.c')
-rw-r--r-- | sys/dev/patm/if_patm_attach.c | 1067 |
1 files changed, 1067 insertions, 0 deletions
diff --git a/sys/dev/patm/if_patm_attach.c b/sys/dev/patm/if_patm_attach.c new file mode 100644 index 000000000000..932bb6fb3e28 --- /dev/null +++ b/sys/dev/patm/if_patm_attach.c @@ -0,0 +1,1067 @@ +/* + * Copyright (c) 2003 + * Fraunhofer Institute for Open Communication Systems (FhG Fokus). + * 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. + * + * Author: Hartmut Brandt <harti@freebsd.org> + * + * Driver for IDT77252 based cards like ProSum's. + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_natm.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/errno.h> +#include <sys/conf.h> +#include <sys/module.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/sysctl.h> +#include <sys/queue.h> +#include <sys/condvar.h> +#include <vm/uma.h> + +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/if_atm.h> +#include <net/route.h> +#ifdef ENABLE_BPF +#include <net/bpf.h> +#endif +#include <netinet/in.h> +#include <netinet/if_atm.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/mbpool.h> +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#include <dev/utopia/utopia.h> +#include <dev/patm/idt77252reg.h> +#include <dev/patm/if_patmvar.h> + +MODULE_DEPEND(patm, utopia, 1, 1, 1); +MODULE_DEPEND(patm, pci, 1, 1, 1); +MODULE_DEPEND(patm, atm, 1, 1, 1); +MODULE_DEPEND(patm, libmbpool, 1, 1, 1); + +devclass_t patm_devclass; + +static int patm_probe(device_t dev); +static int patm_attach(device_t dev); +static int patm_detach(device_t dev); +static device_method_t patm_methods[] = { + DEVMETHOD(device_probe, patm_probe), + DEVMETHOD(device_attach, patm_attach), + DEVMETHOD(device_detach, patm_detach), + {0,0} +}; +static driver_t patm_driver = { + "patm", + patm_methods, + sizeof(struct patm_softc), +}; +DRIVER_MODULE(patm, pci, patm_driver, patm_devclass, NULL, 0); + +static const struct { + u_int devid; + const char *desc; +} devs[] = { + { PCI_DEVICE_IDT77252, "NICStAR (77222/77252) ATM adapter" }, + { PCI_DEVICE_IDT77v252, "NICStAR (77v252) ATM adapter" }, + { PCI_DEVICE_IDT77v222, "NICStAR (77v222) ATM adapter" }, + { 0, NULL } +}; + +SYSCTL_DECL(_hw_atm); + +static int patm_phy_readregs(struct ifatm *, u_int, uint8_t *, u_int *); +static int patm_phy_writereg(struct ifatm *, u_int, u_int, u_int); +static const struct utopia_methods patm_utopia_methods = { + patm_phy_readregs, + patm_phy_writereg +}; + +static void patm_destroy(struct patm_softc *sc); + +static int patm_sysctl_istats(SYSCTL_HANDLER_ARGS); +static int patm_sysctl_eeprom(SYSCTL_HANDLER_ARGS); + +static void patm_read_eeprom(struct patm_softc *sc); +static int patm_sq_init(struct patm_softc *sc); +static int patm_rbuf_init(struct patm_softc *sc); +static int patm_txmap_init(struct patm_softc *sc); + +static void patm_env_getuint(struct patm_softc *, u_int *, const char *); + +#ifdef PATM_DEBUG +static int patm_sysctl_regs(SYSCTL_HANDLER_ARGS); +static int patm_sysctl_tsq(SYSCTL_HANDLER_ARGS); +int patm_dump_vc(u_int unit, u_int vc) __unused; +int patm_dump_regs(u_int unit) __unused; +int patm_dump_sram(u_int unit, u_int from, u_int words) __unused; +#endif + +/* + * Probe for a IDT77252 controller + */ +static int +patm_probe(device_t dev) +{ + u_int i; + + if (pci_get_vendor(dev) == PCI_VENDOR_IDT) { + for (i = 0; devs[i].desc != NULL; i++) + if (pci_get_device(dev) == devs[i].devid) { + device_set_desc(dev, devs[i].desc); + return (0); + } + } + return (ENXIO); +} + +/* + * Attach + */ +static int +patm_attach(device_t dev) +{ + struct patm_softc *sc; + int error; + struct ifnet *ifp; + int rid; + u_int a; + + static const struct idt_mmap idt_mmap[4] = IDT_MMAP; + + sc = device_get_softc(dev); + + sc->dev = dev; +#ifdef IATM_DEBUG + sc->debug = IATM_DEBUG; +#endif + sc->ifatm.mib.device = ATM_DEVICE_IDTABR25; + sc->ifatm.mib.serial = 0; + sc->ifatm.mib.hw_version = 0; + sc->ifatm.mib.sw_version = 0; + sc->ifatm.mib.vpi_bits = PATM_VPI_BITS; + sc->ifatm.mib.vci_bits = 0; /* set below */; + sc->ifatm.mib.max_vpcs = 0; + sc->ifatm.mib.max_vccs = 0; /* set below */ + sc->ifatm.mib.media = IFM_ATM_UNKNOWN; + sc->ifatm.phy = &sc->utopia; + + ifp = &sc->ifatm.ifnet; + ifp->if_softc = sc; + ifp->if_unit = device_get_unit(dev); + ifp->if_name = "patm"; + ifp->if_flags = IFF_SIMPLEX; + ifp->if_watchdog = NULL; + ifp->if_init = patm_init; + ifp->if_ioctl = patm_ioctl; + ifp->if_start = patm_start; + ifp->if_watchdog = NULL; + + /* do this early so we can destroy unconditionally */ + mtx_init(&sc->mtx, device_get_nameunit(dev), + MTX_NETWORK_LOCK, MTX_DEF); + mtx_init(&sc->tst_lock, "tst lock", NULL, MTX_DEF); + cv_init(&sc->vcc_cv, "vcc_close"); + + callout_init(&sc->tst_callout, 1); + + sysctl_ctx_init(&sc->sysctl_ctx); + + /* + * Get revision + */ + sc->revision = pci_read_config(dev, PCIR_REVID, 4) & 0xf; + + /* + * Enable PCI bus master and memory + */ + pci_enable_busmaster(dev); + + rid = IDT_PCI_REG_MEMBASE; + sc->memres = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (sc->memres == NULL) { + patm_printf(sc, "could not map memory\n"); + error = ENXIO; + goto fail; + } + sc->memh = rman_get_bushandle(sc->memres); + sc->memt = rman_get_bustag(sc->memres); + + /* + * Allocate the interrupt (enable it later) + */ + sc->irqid = 0; + sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, + 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); + if (sc->irqres == 0) { + patm_printf(sc, "could not allocate irq\n"); + error = ENXIO; + goto fail; + } + + /* + * Construct the sysctl tree + */ + error = ENOMEM; + if ((sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_hw_atm), OID_AUTO, + device_get_nameunit(dev), CTLFLAG_RD, 0, "")) == NULL) + goto fail; + + if (SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "istats", CTLFLAG_RD, sc, 0, patm_sysctl_istats, + "S", "internal statistics") == NULL) + goto fail; + + if (SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "eeprom", CTLFLAG_RD, sc, 0, patm_sysctl_eeprom, + "S", "EEPROM contents") == NULL) + goto fail; + + if (SYSCTL_ADD_UINT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "lbuf_max", CTLFLAG_RD, &sc->lbuf_max, + 0, "maximum number of large receive buffers") == NULL) + goto fail; + patm_env_getuint(sc, &sc->lbuf_max, "lbuf_max"); + + if (SYSCTL_ADD_UINT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "max_txmaps", CTLFLAG_RW, &sc->tx_maxmaps, + 0, "maximum number of TX DMA maps") == NULL) + goto fail; + patm_env_getuint(sc, &sc->tx_maxmaps, "tx_maxmaps"); + +#ifdef PATM_DEBUG + if (SYSCTL_ADD_UINT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "debug", CTLFLAG_RW, &sc->debug, + 0, "debug flags") == NULL) + goto fail; + sc->debug = PATM_DEBUG; + patm_env_getuint(sc, &sc->debug, "debug"); + + if (SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "regs", CTLFLAG_RD, sc, 0, patm_sysctl_regs, + "S", "registers") == NULL) + goto fail; + + if (SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "tsq", CTLFLAG_RD, sc, 0, patm_sysctl_tsq, + "S", "TSQ") == NULL) + goto fail; +#endif + + patm_reset(sc); + + /* + * Detect and attach the phy. + */ + patm_debug(sc, ATTACH, "attaching utopia"); + sc->ifatm.phy = &sc->utopia; + utopia_attach(&sc->utopia, &sc->ifatm, &sc->media, &sc->mtx, + &sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), + &patm_utopia_methods); + + /* + * Start the PHY because we need the autodetection + */ + patm_debug(sc, ATTACH, "starting utopia"); + mtx_lock(&sc->mtx); + utopia_start(&sc->utopia); + utopia_reset(&sc->utopia); + mtx_unlock(&sc->mtx); + + /* Read EEPROM */ + patm_read_eeprom(sc); + + /* analyze it */ + if (strncmp(sc->eeprom + PATM_PROATM_NAME_OFFSET, PATM_PROATM_NAME, + strlen(PATM_PROATM_NAME)) == 0) { + if (sc->utopia.chip->type == UTP_TYPE_IDT77105) { + sc->ifatm.mib.device = ATM_DEVICE_PROATM25; + sc->ifatm.mib.pcr = ATM_RATE_25_6M; + sc->ifatm.mib.media = IFM_ATM_UTP_25; + sc->flags |= PATM_25M; + patm_printf(sc, "ProATM 25 interface; "); + + } else { + /* cannot really know which media */ + sc->ifatm.mib.device = ATM_DEVICE_PROATM155; + sc->ifatm.mib.pcr = ATM_RATE_155M; + sc->ifatm.mib.media = IFM_ATM_MM_155; + patm_printf(sc, "ProATM 155 interface; "); + } + + bcopy(sc->eeprom + PATM_PROATM_MAC_OFFSET, sc->ifatm.mib.esi, + sizeof(sc->ifatm.mib.esi)); + + } else { + if (sc->utopia.chip->type == UTP_TYPE_IDT77105) { + sc->ifatm.mib.device = ATM_DEVICE_IDTABR25; + sc->ifatm.mib.pcr = ATM_RATE_25_6M; + sc->ifatm.mib.media = IFM_ATM_UTP_25; + sc->flags |= PATM_25M; + patm_printf(sc, "IDT77252 25MBit interface; "); + + } else { + /* cannot really know which media */ + sc->ifatm.mib.device = ATM_DEVICE_IDTABR155; + sc->ifatm.mib.pcr = ATM_RATE_155M; + sc->ifatm.mib.media = IFM_ATM_MM_155; + patm_printf(sc, "IDT77252 155MBit interface; "); + } + + bcopy(sc->eeprom + PATM_IDT_MAC_OFFSET, sc->ifatm.mib.esi, + sizeof(sc->ifatm.mib.esi)); + } + printf("idt77252 Rev. %c; %s PHY\n", 'A' + sc->revision, + sc->utopia.chip->name); + + utopia_reset_media(&sc->utopia); + utopia_init_media(&sc->utopia); + + /* + * Determine RAM size + */ + for (a = 0; a < 0x20000; a++) + patm_sram_write(sc, a, 0); + patm_sram_write(sc, 0, 0xdeadbeef); + if (patm_sram_read(sc, 0x4004) == 0xdeadbeef) + sc->mmap = &idt_mmap[0]; + else if (patm_sram_read(sc, 0x8000) == 0xdeadbeef) + sc->mmap = &idt_mmap[1]; + else if (patm_sram_read(sc, 0x20000) == 0xdeadbeef) + sc->mmap = &idt_mmap[2]; + else + sc->mmap = &idt_mmap[3]; + + sc->ifatm.mib.vci_bits = sc->mmap->vcbits - sc->ifatm.mib.vpi_bits; + sc->ifatm.mib.max_vccs = sc->mmap->max_conn; + patm_sram_write(sc, 0, 0); + patm_printf(sc, "%uK x 32 SRAM; %u connections\n", sc->mmap->sram, + sc->mmap->max_conn); + + /* initialize status queues */ + error = patm_sq_init(sc); + if (error != 0) + goto fail; + + /* get TST */ + sc->tst_soft = malloc(sizeof(uint32_t) * sc->mmap->tst_size, + M_DEVBUF, M_WAITOK); + + /* allocate all the receive buffer stuff */ + error = patm_rbuf_init(sc); + if (error != 0) + goto fail; + + /* + * Allocate SCD tag + * + * Don't use BUS_DMA_ALLOCNOW, because we never need bouncing with + * bus_dmamem_alloc() + */ + error = bus_dma_tag_create(NULL, PAGE_SIZE, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, + NULL, NULL, sizeof(struct patm_scd), 1, + sizeof(struct patm_scd), 0, NULL, NULL, &sc->scd_tag); + if (error) { + patm_printf(sc, "SCD DMA tag create %d\n", error); + goto fail; + } + LIST_INIT(&sc->scd_list); + + /* allocate VCC zone and pointers */ + if ((sc->vcc_zone = uma_zcreate("PATM vccs", sizeof(struct patm_vcc), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0)) == NULL) { + patm_printf(sc, "cannot allocate zone for vccs\n"); + goto fail; + } + sc->vccs = malloc(sizeof(sc->vccs[0]) * sc->mmap->max_conn, + M_DEVBUF, M_WAITOK | M_ZERO); + + /* allocate transmission resources */ + error = patm_txmap_init(sc); + if (error != 0) + goto fail; + + /* poll while we are not running */ + sc->utopia.flags |= UTP_FL_POLL_CARRIER; + + patm_debug(sc, ATTACH, "attaching interface"); + atm_ifattach(ifp); + +#ifdef ENABLE_BPF + bpfattach(ifp, DLT_ATM_RFC1483, sizeof(struct atmllc)); +#endif + + patm_debug(sc, ATTACH, "attaching interrupt handler"); + error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_NET, patm_intr, + sc, &sc->ih); + if (error != 0) { + patm_printf(sc, "could not setup interrupt\n"); + atm_ifdetach(&sc->ifatm.ifnet); + goto fail; + } + + return (0); + + fail: + patm_destroy(sc); + return (error); +} + +/* + * Detach + */ +static int +patm_detach(device_t dev) +{ + struct patm_softc *sc; + + sc = (struct patm_softc *)device_get_softc(dev); + + mtx_lock(&sc->mtx); + patm_stop(sc); + if (sc->utopia.state & UTP_ST_ATTACHED) { + patm_debug(sc, ATTACH, "detaching utopia"); + utopia_stop(&sc->utopia); + utopia_detach(&sc->utopia); + } + mtx_unlock(&sc->mtx); + + atm_ifdetach(&sc->ifatm.ifnet); + + patm_destroy(sc); + + return (0); +} + +/* + * Destroy everything. Assume we are stopped. + */ +static void +patm_destroy(struct patm_softc *sc) +{ + u_int i; + struct patm_txmap *map; + + if (sc->ih != NULL) + bus_teardown_intr(sc->dev, sc->irqres, sc->ih); + + if (sc->tx_mapzone != NULL) { + /* all maps must be free */ + while ((map = SLIST_FIRST(&sc->tx_maps_free)) != NULL) { + bus_dmamap_destroy(sc->tx_tag, map->map); + SLIST_REMOVE_HEAD(&sc->tx_maps_free, link); + uma_zfree(sc->tx_mapzone, map); + } + uma_zdestroy(sc->tx_mapzone); + } + + if (sc->scd_tag != NULL) + bus_dma_tag_destroy(sc->scd_tag); + + if (sc->tx_tag != NULL) + bus_dma_tag_destroy(sc->scd_tag); + + if (sc->vccs != NULL) { + for (i = 0; i < sc->mmap->max_conn; i++) + if (sc->vccs[i] != NULL) + uma_zfree(sc->vcc_zone, sc->vccs[i]); + free(sc->vccs, M_DEVBUF); + } + if (sc->vcc_zone != NULL) + uma_zdestroy(sc->vcc_zone); + + if (sc->lbufs != NULL) { + for (i = 0; i < sc->lbuf_max; i++) + bus_dmamap_destroy(sc->lbuf_tag, sc->lbufs[i].map); + free(sc->lbufs, M_DEVBUF); + } + + if (sc->lbuf_tag != NULL) + bus_dma_tag_destroy(sc->lbuf_tag); + + if (sc->sbuf_pool != NULL) + mbp_destroy(sc->sbuf_pool); + if (sc->vbuf_pool != NULL) + mbp_destroy(sc->vbuf_pool); + + if (sc->sbuf_tag != NULL) + bus_dma_tag_destroy(sc->sbuf_tag); + + if (sc->tst_soft != NULL) + free(sc->tst_soft, M_DEVBUF); + + /* + * Free all status queue memory resources + */ + if (sc->tsq != NULL) { + bus_dmamap_unload(sc->sq_tag, sc->sq_map); + bus_dmamem_free(sc->sq_tag, sc->tsq, sc->sq_map); + bus_dma_tag_destroy(sc->sq_tag); + } + + if (sc->irqres != NULL) + bus_release_resource(sc->dev, SYS_RES_IRQ, + sc->irqid, sc->irqres); + if (sc->memres != NULL) + bus_release_resource(sc->dev, SYS_RES_MEMORY, + IDT_PCI_REG_MEMBASE, sc->memres); + + /* this was initialize unconditionally */ + sysctl_ctx_free(&sc->sysctl_ctx); + cv_destroy(&sc->vcc_cv); + mtx_destroy(&sc->tst_lock); + mtx_destroy(&sc->mtx); +} + +/* + * Try to find a variable in the environment and parse it as an unsigned + * integer. + */ +static void +patm_env_getuint(struct patm_softc *sc, u_int *var, const char *name) +{ + char full[IFNAMSIZ + 3 + 20]; + char *val, *end; + u_long u; + + snprintf(full, sizeof(full), "hw.%s.%s", + device_get_nameunit(sc->dev), name); + + if ((val = getenv(full)) != NULL) { + u = strtoul(val, &end, 0); + if (end > val && *end == '\0') { + if (bootverbose) + patm_printf(sc, "%s=%lu\n", full, u); + *var = u; + } + freeenv(val); + } +} + +/* + * Sysctl handler for internal statistics + * + * LOCK: unlocked, needed + */ +static int +patm_sysctl_istats(SYSCTL_HANDLER_ARGS) +{ + struct patm_softc *sc = arg1; + uint32_t *ret; + int error; + + ret = malloc(sizeof(sc->stats), M_TEMP, M_WAITOK); + + mtx_lock(&sc->mtx); + bcopy(&sc->stats, ret, sizeof(sc->stats)); + mtx_unlock(&sc->mtx); + + error = SYSCTL_OUT(req, ret, sizeof(sc->stats)); + free(ret, M_TEMP); + + return (error); +} + +/* + * Sysctl handler for EEPROM + * + * LOCK: unlocked, needed + */ +static int +patm_sysctl_eeprom(SYSCTL_HANDLER_ARGS) +{ + struct patm_softc *sc = arg1; + void *ret; + int error; + + ret = malloc(sizeof(sc->eeprom), M_TEMP, M_WAITOK); + + mtx_lock(&sc->mtx); + bcopy(sc->eeprom, ret, sizeof(sc->eeprom)); + mtx_unlock(&sc->mtx); + + error = SYSCTL_OUT(req, ret, sizeof(sc->eeprom)); + free(ret, M_TEMP); + + return (error); +} + +/* + * Read the EEPROM. We assume that this is a XIRCOM 25020 + */ +static void +patm_read_eeprom(struct patm_softc *sc) +{ + u_int gp; + uint8_t byte; + int i, addr; + + static const uint32_t tab[] = { + /* CS transition to reset the chip */ + IDT_GP_EECS | IDT_GP_EESCLK, 0, + /* read command 0x03 */ + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, IDT_GP_EEDO, + IDT_GP_EESCLK | IDT_GP_EEDO, IDT_GP_EEDO, + IDT_GP_EESCLK | IDT_GP_EEDO, 0, + /* address 0x00 */ + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, 0, + IDT_GP_EESCLK, 0, + }; + + /* go to a known state (chip enabled) */ + gp = patm_nor_read(sc, IDT_NOR_GP); + gp &= ~(IDT_GP_EESCLK | IDT_GP_EECS | IDT_GP_EEDO); + + for (i = 0; i < sizeof(tab) / sizeof(tab[0]); i++) { + patm_nor_write(sc, IDT_NOR_GP, gp | tab[i]); + DELAY(40); + } + + /* read out the prom */ + for (addr = 0; addr < 256; addr++) { + byte = 0; + for (i = 0; i < 8; i++) { + byte <<= 1; + if (patm_nor_read(sc, IDT_NOR_GP) & IDT_GP_EEDI) + byte |= 1; + /* rising CLK */ + patm_nor_write(sc, IDT_NOR_GP, gp | IDT_GP_EESCLK); + DELAY(40); + /* falling clock */ + patm_nor_write(sc, IDT_NOR_GP, gp); + DELAY(40); + } + sc->eeprom[addr] = byte; + } +} + +/* + * PHY access read + */ +static int +patm_phy_readregs(struct ifatm *ifatm, u_int reg, uint8_t *val, u_int *n) +{ + struct patm_softc *sc = ifatm->ifnet.if_softc; + u_int cnt = *n; + + if (reg >= 0x100) + return (EINVAL); + + patm_cmd_wait(sc); + while (reg < 0x100 && cnt > 0) { + patm_nor_write(sc, IDT_NOR_CMD, IDT_MKCMD_RUTIL(1, 0, reg)); + patm_cmd_wait(sc); + *val = patm_nor_read(sc, IDT_NOR_D0); + patm_debug(sc, PHY, "phy(%02x)=%02x", reg, *val); + val++; + reg++; + cnt--; + } + *n = *n - cnt; + return (0); +} + +/* + * Write PHY reg + */ +static int +patm_phy_writereg(struct ifatm *ifatm, u_int reg, u_int mask, u_int val) +{ + struct patm_softc *sc = ifatm->ifnet.if_softc; + u_int old, new; + + if (reg >= 0x100) + return (EINVAL); + + patm_cmd_wait(sc); + patm_nor_write(sc, IDT_NOR_CMD, IDT_MKCMD_RUTIL(1, 0, reg)); + patm_cmd_wait(sc); + + old = patm_nor_read(sc, IDT_NOR_D0); + new = (old & ~mask) | (val & mask); + patm_debug(sc, PHY, "phy(%02x) %02x -> %02x", reg, old, new); + + patm_nor_write(sc, IDT_NOR_D0, new); + patm_nor_write(sc, IDT_NOR_CMD, IDT_MKCMD_WUTIL(1, 0, reg)); + patm_cmd_wait(sc); + + return (0); +} + +/* + * Allocate a large chunk of DMA able memory for the transmit + * and receive status queues. We align this to a page boundary + * to ensure the alignment. + */ +static int +patm_sq_init(struct patm_softc *sc) +{ + int error; + void *p; + + /* compute size of the two queues */ + sc->sq_size = IDT_TSQ_SIZE * IDT_TSQE_SIZE + + PATM_RSQ_SIZE * IDT_RSQE_SIZE + + IDT_RAWHND_SIZE; + + patm_debug(sc, ATTACH, + "allocating status queues (%zu) ...", sc->sq_size); + + /* + * allocate tag + * Don't use BUS_DMA_ALLOCNOW, because we never need bouncing with + * bus_dmamem_alloc() + */ + error = bus_dma_tag_create(NULL, PATM_SQ_ALIGNMENT, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, + NULL, NULL, sc->sq_size, 1, sc->sq_size, + 0, NULL, NULL, &sc->sq_tag); + if (error) { + patm_printf(sc, "memory DMA tag create %d\n", error); + return (error); + } + + /* allocate memory */ + error = bus_dmamem_alloc(sc->sq_tag, &p, 0, &sc->sq_map); + if (error) { + patm_printf(sc, "memory DMA alloc %d\n", error); + bus_dma_tag_destroy(sc->sq_tag); + return (error); + } + + /* map it */ + sc->tsq_phy = 0x1fff; + error = bus_dmamap_load(sc->sq_tag, sc->sq_map, p, + sc->sq_size, patm_load_callback, &sc->tsq_phy, BUS_DMA_NOWAIT); + if (error) { + patm_printf(sc, "memory DMA map load %d\n", error); + bus_dmamem_free(sc->sq_tag, p, sc->sq_map); + bus_dma_tag_destroy(sc->sq_tag); + return (error); + } + + /* set queue start */ + sc->tsq = p; + sc->rsq = (void *)((char *)p + IDT_TSQ_SIZE * IDT_TSQE_SIZE); + sc->rsq_phy = sc->tsq_phy + IDT_TSQ_SIZE * IDT_TSQE_SIZE; + sc->rawhnd = (void *)((char *)sc->rsq + PATM_RSQ_SIZE * IDT_RSQE_SIZE); + sc->rawhnd_phy = sc->rsq_phy + PATM_RSQ_SIZE * IDT_RSQE_SIZE; + + return (0); +} + +/* + * Initialize all receive buffer stuff + */ +static int +patm_rbuf_init(struct patm_softc *sc) +{ + u_int i; + int error; + + patm_debug(sc, ATTACH, "allocating Rx buffer resources ..."); + /* + * Create a tag for small buffers. We allocate these page wise. + * Don't use BUS_DMA_ALLOCNOW, because we never need bouncing with + * bus_dmamem_alloc() + */ + if ((error = bus_dma_tag_create(NULL, PAGE_SIZE, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + SMBUF_PAGE_SIZE, 1, SMBUF_PAGE_SIZE, 0, + NULL, NULL, &sc->sbuf_tag)) != 0) { + patm_printf(sc, "sbuf DMA tag create %d\n", error); + return (error); + } + + error = mbp_create(&sc->sbuf_pool, "patm sbufs", sc->sbuf_tag, + SMBUF_MAX_PAGES, SMBUF_PAGE_SIZE, SMBUF_CHUNK_SIZE); + if (error != 0) { + patm_printf(sc, "smbuf pool create %d\n", error); + return (error); + } + + error = mbp_create(&sc->vbuf_pool, "patm vbufs", sc->sbuf_tag, + VMBUF_MAX_PAGES, SMBUF_PAGE_SIZE, VMBUF_CHUNK_SIZE); + if (error != 0) { + patm_printf(sc, "vmbuf pool create %d\n", error); + return (error); + } + + /* + * Create a tag for large buffers. + * Don't use BUS_DMA_ALLOCNOW, because it makes no sense with multiple + * maps using one tag. Rather use BUS_DMA_NOWAIT when loading the map + * to prevent EINPROGRESS. + */ + if ((error = bus_dma_tag_create(NULL, 4, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + MCLBYTES, 1, MCLBYTES, 0, + NULL, NULL, &sc->lbuf_tag)) != 0) { + patm_printf(sc, "lbuf DMA tag create %d\n", error); + return (error); + } + + if (sc->lbuf_max < IDT_FBQ_SIZE) + sc->lbuf_max = LMBUF_MAX; + sc->lbufs = malloc(sizeof(sc->lbufs[0]) * sc->lbuf_max, + M_DEVBUF, M_ZERO | M_WAITOK); + + SLIST_INIT(&sc->lbuf_free_list); + for (i = 0; i < sc->lbuf_max; i++) { + struct lmbuf *b = &sc->lbufs[i]; + + error = bus_dmamap_create(sc->lbuf_tag, 0, &b->map); + if (error) { + /* must deallocate here, because a test for NULL + * does not work on most archs */ + while (i-- > 0) + bus_dmamap_destroy(sc->lbuf_tag, + sc->lbufs[i].map); + free(sc->lbufs, M_DEVBUF); + sc->lbufs = NULL; + return (error); + } + b->handle = i; + SLIST_INSERT_HEAD(&sc->lbuf_free_list, b, link); + } + + return (0); +} + +/* + * Allocate everything needed for the transmission maps. + */ +static int +patm_txmap_init(struct patm_softc *sc) +{ + int error; + struct patm_txmap *map; + + /* get transmission tag */ + error = bus_dma_tag_create(NULL, 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, + NULL, NULL, 65536, IDT_SCQ_SIZE - 1, 65536, + 0, NULL, NULL, &sc->tx_tag); + if (error) { + patm_printf(sc, "cannot allocate TX tag %d\n", error); + return (error); + } + + if ((sc->tx_mapzone = uma_zcreate("PATM tx maps", + sizeof(struct patm_txmap), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0)) == NULL) + return (ENOMEM); + + if (sc->tx_maxmaps < PATM_CFG_TXMAPS_MAX) + sc->tx_maxmaps = PATM_CFG_TXMAPS_MAX; + sc->tx_nmaps = PATM_CFG_TXMAPS_INIT; + + for (sc->tx_nmaps = 0; sc->tx_nmaps < PATM_CFG_TXMAPS_INIT; + sc->tx_nmaps++) { + map = uma_zalloc(sc->tx_mapzone, M_WAITOK); + error = bus_dmamap_create(sc->tx_tag, 0, &map->map); + if (error) { + uma_zfree(sc->tx_mapzone, map); + return (ENOMEM); + } + SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link); + } + + return (0); +} + +#ifdef PATM_DEBUG + +/* + * Sysctl handler for REGS + * + * LOCK: unlocked, needed + */ +static int +patm_sysctl_regs(SYSCTL_HANDLER_ARGS) +{ + struct patm_softc *sc = arg1; + uint32_t *ret; + int error, i; + + ret = malloc(IDT_NOR_END, M_TEMP, M_WAITOK); + + mtx_lock(&sc->mtx); + for (i = 0; i < IDT_NOR_END; i += 4) + ret[i / 4] = patm_nor_read(sc, i); + mtx_unlock(&sc->mtx); + + error = SYSCTL_OUT(req, ret, IDT_NOR_END); + free(ret, M_TEMP); + + return (error); +} + +/* + * Sysctl handler for TSQ + * + * LOCK: unlocked, needed + */ +static int +patm_sysctl_tsq(SYSCTL_HANDLER_ARGS) +{ + struct patm_softc *sc = arg1; + void *ret; + int error; + + ret = malloc(IDT_TSQ_SIZE * IDT_TSQE_SIZE, M_TEMP, M_WAITOK); + + mtx_lock(&sc->mtx); + memcpy(ret, sc->tsq, IDT_TSQ_SIZE * IDT_TSQE_SIZE); + mtx_unlock(&sc->mtx); + + error = SYSCTL_OUT(req, ret, IDT_TSQ_SIZE * IDT_TSQE_SIZE); + free(ret, M_TEMP); + + return (error); +} + +/* + * debugging + */ +static struct patm_softc * +patm_dump_unit(u_int unit) +{ + devclass_t dc; + struct patm_softc *sc; + + dc = devclass_find("patm"); + if (dc == NULL) { + printf("%s: can't find devclass\n", __func__); + return (NULL); + } + sc = devclass_get_softc(dc, unit); + if (sc == NULL) { + printf("%s: invalid unit number: %d\n", __func__, unit); + return (NULL); + } + return (sc); +} + +int +patm_dump_vc(u_int unit, u_int vc) +{ + struct patm_softc *sc; + uint32_t tct[8]; + uint32_t rct[4]; + uint32_t scd[12]; + u_int i; + + if ((sc = patm_dump_unit(unit)) == NULL) + return (0); + + for (i = 0; i < 8; i++) + tct[i] = patm_sram_read(sc, vc * 8 + i); + for (i = 0; i < 4; i++) + rct[i] = patm_sram_read(sc, sc->mmap->rct + vc * 4 + i); + for (i = 0; i < 12; i++) + scd[i] = patm_sram_read(sc, (tct[0] & 0x7ffff) + i); + + printf("TCT%3u: %08x %08x %08x %08x %08x %08x %08x %08x\n", vc, + tct[0], tct[1], tct[2], tct[3], tct[4], tct[5], tct[6], tct[7]); + printf("RCT%3u: %08x %08x %08x %08x\n", vc, + rct[0], rct[1], rct[2], rct[3]); + printf("SCD%3u: %08x %08x %08x %08x %08x %08x %08x %08x\n", vc, + scd[0], scd[1], scd[2], scd[3], scd[4], scd[5], scd[6], scd[7]); + printf(" %08x %08x %08x %08x\n", + scd[8], scd[9], scd[10], scd[11]); + + return (0); +} + +int +patm_dump_regs(u_int unit) +{ + struct patm_softc *sc; + u_int i; + + if ((sc = patm_dump_unit(unit)) == NULL) + return (0); + + for (i = 0; i <= IDT_NOR_DNOW; i += 4) + printf("%x: %08x\n", i, patm_nor_read(sc, i)); + + return (0); +} + +int +patm_dump_sram(u_int unit, u_int from, u_int words) +{ + struct patm_softc *sc; + u_int i; + + if ((sc = patm_dump_unit(unit)) == NULL) + return (0); + + for (i = 0; i < words; i++) { + if (i % 8 == 0) + printf("%05x:", from + i); + printf(" %08x", patm_sram_read(sc, from + i)); + if (i % 8 == 7) + printf("\n"); + } + if (i % 8 != 0) + printf("\n"); + return (0); +} +#endif |