--- src/sys/dev/netif/lnc/if_lnc_pci.c 2005/12/31 14:07:59 1.9 +++ src/sys/dev/netif/lnc/if_lnc_pci.c 2006/07/07 14:16:29 1.10 @@ -1,24 +1,68 @@ -/* - * Copyright (c) 1994-2000 - * Paul Richards. All rights reserved. +/* $NetBSD: if_le_pci.c,v 1.43 2005/12/11 12:22:49 christos Exp $ */ +/* $FreeBSD: src/sys/dev/le/if_le_pci.c,v 1.4 2006/06/05 15:14:14 marius Exp $ */ +/* $DragonFly$ */ + + +/*- + * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. * * 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, - * verbatim and that no modifications are made prior to this - * point in the file. + * 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. The name Paul Richards may not be used to endorse or promote products - * derived from this software without specific prior written permission. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY PAUL RICHARDS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 PAUL RICHARDS BE LIABLE + * 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) @@ -27,230 +71,455 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/lnc/if_lnc_pci.c,v 1.25 2001/07/04 13:00:19 nyan Exp $ - * $DragonFly$ + * @(#)if_le.c 8.2 (Berkeley) 11/16/93 */ #include #include -#include -#include -#include -#include - -#include -#include #include +#include +#include +#include +#include +#include #include - -#include +#include #include #include +#include #include +#include +#include + #include #include -#include -#include +#include +#include +#include + +#define AMD_VENDOR 0x1022 +#define AMD_PCNET_PCI 0x2000 +#define AMD_PCNET_HOME 0x2001 +#define PCNET_MEMSIZE (32*1024) +#define PCNET_PCI_RDP 0x10 +#define PCNET_PCI_RAP 0x12 +#define PCNET_PCI_BDP 0x16 + +#define LNC_PROBE_PRIORITY -100 + +struct le_pci_softc { + struct am79900_softc sc_am79900; /* glue to MI code */ + + 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; + + bus_dma_tag_t sc_pdmat; + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_dmam; +}; -#define AMD_VENDOR_ID 0x1022 -#define PCI_DEVICE_ID_PCNet_PCI 0x2000 -#define PCI_DEVICE_ID_PCHome_PCI 0x2001 +static device_probe_t le_pci_probe; +static device_attach_t le_pci_attach; +static device_detach_t le_pci_detach; +static device_resume_t le_pci_resume; +static device_suspend_t le_pci_suspend; + +static device_method_t le_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, le_pci_probe), + DEVMETHOD(device_attach, le_pci_attach), + DEVMETHOD(device_detach, le_pci_detach), + /* We can just use the suspend method here. */ + DEVMETHOD(device_shutdown, le_pci_suspend), + DEVMETHOD(device_suspend, le_pci_suspend), + DEVMETHOD(device_resume, le_pci_resume), -#define LNC_PROBE_PRIORITY -1 + { 0, 0 } +}; + +DEFINE_CLASS_0(lnc, le_pci_driver, le_pci_methods, sizeof(struct le_pci_softc)); +DRIVER_MODULE(lnc, pci, le_pci_driver, le_devclass, 0, 0); +MODULE_DEPEND(lnc, ether, 1, 1, 1); + +static const int le_home_supmedia[] = { + IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0, 0) +}; + +static const int le_pci_supmedia[] = { + IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0), + IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, IFM_FDX, 0), + IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0), + IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, 0), + IFM_MAKEWORD(IFM_ETHER, IFM_10_5, 0, 0), + IFM_MAKEWORD(IFM_ETHER, IFM_10_5, IFM_FDX, 0) +}; + +static void le_pci_wrbcr(struct lance_softc *, uint16_t, uint16_t); +static uint16_t le_pci_rdbcr(struct lance_softc *, uint16_t); +static void le_pci_wrcsr(struct lance_softc *, uint16_t, uint16_t); +static uint16_t le_pci_rdcsr(struct lance_softc *, uint16_t); +static int le_pci_mediachange(struct lance_softc *); +static void le_pci_hwreset(struct lance_softc *); +static bus_dmamap_callback_t le_pci_dma_callback; -static int lnc_pci_detach(device_t); +static void +le_pci_wrbcr(struct lance_softc *sc, uint16_t port, uint16_t val) +{ + struct le_pci_softc *lesc = (struct le_pci_softc *)sc; + + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, port); + bus_space_barrier(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, 2, + BUS_SPACE_BARRIER_WRITE); + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_BDP, val); +} + +static uint16_t +le_pci_rdbcr(struct lance_softc *sc, uint16_t port) +{ + struct le_pci_softc *lesc = (struct le_pci_softc *)sc; + + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, port); + bus_space_barrier(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, 2, + BUS_SPACE_BARRIER_WRITE); + return (bus_space_read_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_BDP)); +} + +static void +le_pci_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val) +{ + struct le_pci_softc *lesc = (struct le_pci_softc *)sc; + + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, port); + bus_space_barrier(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, 2, + BUS_SPACE_BARRIER_WRITE); + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RDP, val); +} + +static uint16_t +le_pci_rdcsr(struct lance_softc *sc, uint16_t port) +{ + struct le_pci_softc *lesc = (struct le_pci_softc *)sc; + + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, port); + bus_space_barrier(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, 2, + BUS_SPACE_BARRIER_WRITE); + return (bus_space_read_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RDP)); +} static int -lnc_pci_probe(device_t dev) +le_pci_mediachange(struct lance_softc *sc) { - if (pci_get_vendor(dev) != AMD_VENDOR_ID) - return (ENXIO); + struct ifmedia *ifm = &sc->sc_media; + uint16_t reg; - switch(pci_get_device(dev)) { - case PCI_DEVICE_ID_PCNet_PCI: - device_set_desc(dev, "PCNet/PCI Ethernet adapter"); - return(LNC_PROBE_PRIORITY); - break; - case PCI_DEVICE_ID_PCHome_PCI: - device_set_desc(dev, "PCHome/PCI Ethernet adapter"); - return(LNC_PROBE_PRIORITY); - break; - default: - return (ENXIO); - break; + if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) + return (EINVAL); + + if (IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) + le_pci_wrbcr(sc, LE_BCR49, + (le_pci_rdbcr(sc, LE_BCR49) & ~LE_B49_PHYSEL) | 0x1); + else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) + le_pci_wrbcr(sc, LE_BCR2, + le_pci_rdbcr(sc, LE_BCR2) | LE_B2_ASEL); + else { + le_pci_wrbcr(sc, LE_BCR2, + le_pci_rdbcr(sc, LE_BCR2) & ~LE_B2_ASEL); + + reg = le_pci_rdcsr(sc, LE_CSR15); + reg &= ~LE_C15_PORTSEL(LE_PORTSEL_MASK); + if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) + reg |= LE_C15_PORTSEL(LE_PORTSEL_10T); + else + reg |= LE_C15_PORTSEL(LE_PORTSEL_AUI); + le_pci_wrcsr(sc, LE_CSR15, reg); } - return (ENXIO); + + reg = le_pci_rdbcr(sc, LE_BCR9); + if (IFM_OPTIONS(ifm->ifm_media) & IFM_FDX) { + reg |= LE_B9_FDEN; + /* + * Allow FDX on AUI only if explicitly chosen, + * not in autoselect mode. + */ + if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_5) + reg |= LE_B9_AUIFD; + else + reg &= ~LE_B9_AUIFD; + } else + reg &= ~LE_B9_FDEN; + le_pci_wrbcr(sc, LE_BCR9, reg); + + return (0); } static void -lnc_alloc_callback(void *arg, bus_dma_segment_t *seg, int nseg, int error) +le_pci_hwreset(struct lance_softc *sc) { - /* Do nothing */ - return; + + /* + * Chip is stopped. Set software style to PCnet-PCI (32-bit). + * Actually, am79900.c implements ILACC support (hence its + * name) but unfortunately VMware does not. As far as this + * driver is concerned that should not make a difference + * though, as the settings used have the same meaning for + * both, ILACC and PCnet-PCI (note that there would be a + * difference for the ADD_FCS/NO_FCS bit if used). + */ + le_pci_wrbcr(sc, LE_BCR20, LE_B20_SSTYLE_PCNETPCI2); +} + +static void +le_pci_dma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct lance_softc *sc = (struct lance_softc *)xsc; + + if (error != 0) + return; + KASSERT(nsegs == 1, ("%s: bad DMA segment count", __func__)); + sc->sc_addr = segs[0].ds_addr; } static int -lnc_pci_attach(device_t dev) +le_pci_probe(device_t dev) { - lnc_softc_t *sc = device_get_softc(dev); - unsigned command; - int rid = 0; - int error = 0; - bus_size_t lnc_mem_size; - - command = pci_read_config(dev, PCIR_COMMAND, 4); - command |= PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN; - pci_write_config(dev, PCIR_COMMAND, command, 4); - - rid = PCIR_MAPS; - sc->portres = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, - RF_ACTIVE); - if (! sc->portres) { - device_printf(dev, "Cannot allocate I/O ports\n"); - error = ENXIO; - goto fail; + if (pci_get_vendor(dev) != AMD_VENDOR) + return (ENXIO); + + switch (pci_get_device(dev)) { + case AMD_PCNET_PCI: + device_set_desc(dev, "AMD PCnet-PCI"); + /* Let pcn(4) win. */ + return (LNC_PROBE_PRIORITY); + case AMD_PCNET_HOME: + device_set_desc(dev, "AMD PCnet-Home"); + /* Let pcn(4) win. */ + return (LNC_PROBE_PRIORITY); + default: + return (ENXIO); } +} - rid = 0; - sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, - RF_ACTIVE | RF_SHAREABLE); +static int +le_pci_attach(device_t dev) +{ + struct le_pci_softc *lesc; + struct lance_softc *sc; + int error, i; + + lesc = device_get_softc(dev); + sc = &lesc->sc_am79900.lsc; + + pci_enable_busmaster(dev); + pci_enable_io(dev, PCIM_CMD_PORTEN); + + lesc->sc_rrid = PCIR_BAR(0); + lesc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &lesc->sc_rrid, RF_ACTIVE); + if (lesc->sc_rres == NULL) { + device_printf(dev, "cannot allocate registers\n"); + error = ENXIO; + goto fail_mtx; + } + lesc->sc_regt = rman_get_bustag(lesc->sc_rres); + lesc->sc_regh = rman_get_bushandle(lesc->sc_rres); - if (! sc->irqres) { - device_printf(dev, "Cannot allocate irq\n"); + 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; + goto fail_rres; } - sc->lnc_btag = rman_get_bustag(sc->portres); - sc->lnc_bhandle = rman_get_bushandle(sc->portres); + error = bus_dma_tag_create( + NULL, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ + 0, /* nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + BUS_DMA_WAITOK, /* flags */ + &lesc->sc_pdmat); + if (error != 0) { + device_printf(dev, "cannot allocate parent DMA tag\n"); + goto fail_ires; + } - /* XXX temp setting for nic */ - sc->nic.ic = PCnet_PCI; - sc->nic.ident = NE2100; - sc->nic.mem_mode = DMA_FIXED; - sc->nrdre = NRDRE; - sc->ntdre = NTDRE; - sc->rap = PCNET_RAP; - sc->rdp = PCNET_RDP; - sc->bdp = PCNET_BDP; - - /* Create a DMA tag describing the ring memory we need */ - - lnc_mem_size = ((NDESC(sc->nrdre) + NDESC(sc->ntdre)) * - sizeof(struct host_ring_entry)); - - lnc_mem_size += sizeof(struct init_block) + (sizeof(struct mds) * - (NDESC(sc->nrdre) + NDESC(sc->ntdre))) + MEM_SLEW; - - lnc_mem_size += (NDESC(sc->nrdre) * RECVBUFSIZE) + - (NDESC(sc->ntdre) * TRANSBUFSIZE); - - error = bus_dma_tag_create(NULL, /* parent */ - 1, /* alignement */ - 0, /* boundary */ - BUS_SPACE_MAXADDR_24BIT, /* lowaddr */ - BUS_SPACE_MAXADDR, /* highaddr */ - NULL, NULL, /* filter, filterarg */ - lnc_mem_size, /* segsize */ - 1, /* nsegments */ - BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ - BUS_DMA_ALLOCNOW, /* flags */ - &sc->dmat); - - if (error) { - device_printf(dev, "Can't create DMA tag\n"); - goto fail; - } - - error = bus_dmamem_alloc(sc->dmat, (void **)&sc->recv_ring, - BUS_DMA_WAITOK, &sc->dmamap); - if (error) { - device_printf(dev, "Couldn't allocate memory\n"); - goto fail; - } - - error = bus_dmamap_load(sc->dmat, sc->dmamap, sc->recv_ring, - lnc_mem_size, lnc_alloc_callback, - sc->recv_ring, 0); - if (error) { - device_printf(dev, "Couldn't map receive ring\n"); - goto fail; - } - - /* Call generic attach code */ - if (! lnc_attach_common(dev)) { - device_printf(dev, "Generic attach code failed\n"); - error = ENXIO; - goto fail; + sc->sc_memsize = PCNET_MEMSIZE; + /* + * For Am79C970A, Am79C971 and Am79C978 the init block must be 2-byte + * aligned and the ring descriptors must be 16-byte aligned when using + * a 32-bit software style. + */ + error = bus_dma_tag_create( + lesc->sc_pdmat, /* parent */ + 16, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + sc->sc_memsize, /* maxsize */ + 1, /* nsegments */ + sc->sc_memsize, /* maxsegsize */ + BUS_DMA_WAITOK, /* flags */ + &lesc->sc_dmat); + if (error != 0) { + device_printf(dev, "cannot allocate buffer DMA tag\n"); + goto fail_pdtag; } - error = bus_setup_intr(dev, sc->irqres, INTR_NETSAFE, lncintr, - sc, &sc->intrhand, - sc->arpcom.ac_if.if_serializer); - if (error) { - device_printf(dev, "Cannot setup irq handler\n"); - ether_ifdetach(&sc->arpcom.ac_if); - goto fail; + error = bus_dmamem_alloc(lesc->sc_dmat, (void **)&sc->sc_mem, + BUS_DMA_WAITOK | BUS_DMA_COHERENT, &lesc->sc_dmam); + if (error != 0) { + device_printf(dev, "cannot allocate DMA buffer memory\n"); + goto fail_dtag; + } + + sc->sc_addr = 0; + error = bus_dmamap_load(lesc->sc_dmat, lesc->sc_dmam, sc->sc_mem, + sc->sc_memsize, le_pci_dma_callback, sc, 0); + if (error != 0 || sc->sc_addr == 0) { + device_printf(dev, "cannot load DMA buffer map\n"); + goto fail_dmem; + } + + sc->sc_flags = LE_BSWAP; + sc->sc_conf3 = 0; + + sc->sc_mediastatus = NULL; + switch (pci_get_device(dev)) { + case AMD_PCNET_HOME: + sc->sc_mediachange = le_pci_mediachange; + sc->sc_supmedia = le_home_supmedia; + sc->sc_nsupmedia = sizeof(le_home_supmedia) / sizeof(int); + sc->sc_defaultmedia = le_home_supmedia[0]; + break; + default: + sc->sc_mediachange = le_pci_mediachange; + sc->sc_supmedia = le_pci_supmedia; + sc->sc_nsupmedia = sizeof(le_pci_supmedia) / sizeof(int); + sc->sc_defaultmedia = le_pci_supmedia[0]; + } + + /* + * Extract the physical MAC address from the ROM. + */ + for (i = 0; i < sizeof(sc->sc_enaddr); i++) + sc->sc_enaddr[i] = + bus_space_read_1(lesc->sc_regt, lesc->sc_regh, i); + + sc->sc_copytodesc = lance_copytobuf_contig; + sc->sc_copyfromdesc = lance_copyfrombuf_contig; + sc->sc_copytobuf = lance_copytobuf_contig; + sc->sc_copyfrombuf = lance_copyfrombuf_contig; + sc->sc_zerobuf = lance_zerobuf_contig; + + sc->sc_rdcsr = le_pci_rdcsr; + sc->sc_wrcsr = le_pci_wrcsr; + sc->sc_hwreset = le_pci_hwreset; + sc->sc_hwinit = NULL; + sc->sc_hwintr = NULL; + sc->sc_nocarrier = NULL; + + error = am79900_config(&lesc->sc_am79900, device_get_name(dev), + device_get_unit(dev)); + if (error != 0) { + device_printf(dev, "cannot attach Am79900\n"); + goto fail_dmap; + } + + error = bus_setup_intr(dev, lesc->sc_ires, INTR_NETSAFE | INTR_MPSAFE, + am79900_intr, sc, &lesc->sc_ih, sc->ifp->if_serializer); + if (error != 0) { + device_printf(dev, "cannot set up interrupt\n"); + goto fail_am79900; } return (0); -fail: - lnc_pci_detach(dev); - return(error); + fail_am79900: + am79900_detach(&lesc->sc_am79900); + fail_dmap: + bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam); + fail_dmem: + bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam); + fail_dtag: + bus_dma_tag_destroy(lesc->sc_dmat); + fail_pdtag: + bus_dma_tag_destroy(lesc->sc_pdmat); + fail_ires: + bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires); + fail_rres: + bus_release_resource(dev, SYS_RES_IOPORT, lesc->sc_rrid, lesc->sc_rres); + fail_mtx: + return (error); } static int -lnc_pci_detach(device_t dev) +le_pci_detach(device_t dev) { - lnc_softc_t *sc = device_get_softc(dev); + struct le_pci_softc *lesc; + struct lance_softc *sc; + lesc = device_get_softc(dev); + sc = &lesc->sc_am79900.lsc; if (device_is_attached(dev)) { - lwkt_serialize_enter(sc->arpcom.ac_if.if_serializer); - lnc_stop(sc); - bus_teardown_intr(dev, sc->irqres, sc->intrhand); - lwkt_serialize_exit(sc->arpcom.ac_if.if_serializer); - - ether_ifdetach(&sc->arpcom.ac_if); - } - - if (sc->irqres) - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irqres); - if (sc->portres) - bus_release_resource(dev, SYS_RES_IOPORT, PCIR_MAPS, - sc->portres); - - if (sc->dmamap) { - bus_dmamap_unload(sc->dmat, sc->dmamap); - bus_dmamem_free(sc->dmat, sc->recv_ring, sc->dmamap); + lwkt_serialize_enter(sc->ifp->if_serializer); + lance_stop(sc); + bus_teardown_intr(dev, lesc->sc_ires, lesc->sc_ih); + lwkt_serialize_exit(sc->ifp->if_serializer); + + am79900_detach(&lesc->sc_am79900); + } + + if (lesc->sc_ires) + bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires); + if (lesc->sc_rres) + bus_release_resource(dev, SYS_RES_IOPORT, lesc->sc_rrid, lesc->sc_rres); + if (lesc->sc_dmam) { + bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam); + bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam); } - if (sc->dmat) - bus_dma_tag_destroy(sc->dmat); + if (lesc->sc_dmat) + bus_dma_tag_destroy(lesc->sc_dmat); + if (lesc->sc_pdmat) + bus_dma_tag_destroy(lesc->sc_pdmat); return (0); } -static device_method_t lnc_pci_methods[] = { - DEVMETHOD(device_probe, lnc_pci_probe), - DEVMETHOD(device_attach, lnc_pci_attach), - DEVMETHOD(device_detach, lnc_pci_detach), -#ifdef notyet - DEVMETHOD(device_suspend, lnc_pci_suspend), - DEVMETHOD(device_resume, lnc_pci_resume), - DEVMETHOD(device_shutdown, lnc_pci_shutdown), -#endif - { 0, 0 } -}; +static int +le_pci_suspend(device_t dev) +{ + struct le_pci_softc *lesc; -static driver_t lnc_pci_driver = { - "lnc", - lnc_pci_methods, - sizeof(struct lnc_softc), -}; + lesc = device_get_softc(dev); -DRIVER_MODULE(if_lnc, pci, lnc_pci_driver, lnc_devclass, 0, 0); + lance_suspend(&lesc->sc_am79900.lsc); + + return (0); +} + +static int +le_pci_resume(device_t dev) +{ + struct le_pci_softc *lesc; + + lesc = device_get_softc(dev); + + lance_resume(&lesc->sc_am79900.lsc); + + return (0); +}