7fe67ebc21
when this driver was changed to use the MAC-independent MII code. This "PHY" does not have a programming interface, but simply senses the duplex mode from the link partner. Since it is not possible to read the 80c24 to determine the link type or status, we consider this to be "manual".
1772 lines
46 KiB
C
1772 lines
46 KiB
C
/* $NetBSD: if_fxp.c,v 1.24 1998/11/03 05:47:38 thorpej Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* 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) 1995, David Greenman
|
|
* 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 unmodified, 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.
|
|
*
|
|
* Id: if_fxp.c,v 1.47 1998/01/08 23:42:29 eivind Exp
|
|
*/
|
|
|
|
/*
|
|
* Intel EtherExpress Pro/100B PCI Fast Ethernet driver
|
|
*/
|
|
|
|
#include "opt_inet.h"
|
|
#include "opt_ns.h"
|
|
#include "bpfilter.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/device.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_media.h>
|
|
#include <net/if_ether.h>
|
|
|
|
#if NBPFILTER > 0
|
|
#include <net/bpf.h>
|
|
#endif
|
|
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_inarp.h>
|
|
#endif
|
|
|
|
#ifdef NS
|
|
#include <netns/ns.h>
|
|
#include <netns/ns_if.h>
|
|
#endif
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/intr.h>
|
|
|
|
#include <dev/mii/miivar.h>
|
|
|
|
#include <dev/pci/if_fxpreg.h>
|
|
#include <dev/pci/if_fxpvar.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcireg.h>
|
|
#include <dev/pci/pcidevs.h>
|
|
|
|
/*
|
|
* NOTE! On the Alpha, we have an alignment constraint. The
|
|
* card DMAs the packet immediately following the RFA. However,
|
|
* the first thing in the packet is a 14-byte Ethernet header.
|
|
* This means that the packet is misaligned. To compensate,
|
|
* we actually offset the RFA 2 bytes into the cluster. This
|
|
* alignes the packet after the Ethernet header at a 32-bit
|
|
* boundary. HOWEVER! This means that the RFA is misaligned!
|
|
*/
|
|
#define RFA_ALIGNMENT_FUDGE 2
|
|
|
|
/*
|
|
* Template for default configuration parameters.
|
|
* See struct fxp_cb_config for the bit definitions.
|
|
*/
|
|
static u_int8_t fxp_cb_config_template[] = {
|
|
0x0, 0x0, /* cb_status */
|
|
0x80, 0x2, /* cb_command */
|
|
0xff, 0xff, 0xff, 0xff, /* link_addr */
|
|
0x16, /* 0 */
|
|
0x8, /* 1 */
|
|
0x0, /* 2 */
|
|
0x0, /* 3 */
|
|
0x0, /* 4 */
|
|
0x80, /* 5 */
|
|
0xb2, /* 6 */
|
|
0x3, /* 7 */
|
|
0x1, /* 8 */
|
|
0x0, /* 9 */
|
|
0x26, /* 10 */
|
|
0x0, /* 11 */
|
|
0x60, /* 12 */
|
|
0x0, /* 13 */
|
|
0xf2, /* 14 */
|
|
0x48, /* 15 */
|
|
0x0, /* 16 */
|
|
0x40, /* 17 */
|
|
0xf3, /* 18 */
|
|
0x0, /* 19 */
|
|
0x3f, /* 20 */
|
|
0x5 /* 21 */
|
|
};
|
|
|
|
static void fxp_mii_initmedia __P((struct fxp_softc *));
|
|
static int fxp_mii_mediachange __P((struct ifnet *));
|
|
static void fxp_mii_mediastatus __P((struct ifnet *, struct ifmediareq *));
|
|
|
|
static void fxp_80c24_initmedia __P((struct fxp_softc *));
|
|
static int fxp_80c24_mediachange __P((struct ifnet *));
|
|
static void fxp_80c24_mediastatus __P((struct ifnet *, struct ifmediareq *));
|
|
|
|
static inline void fxp_scb_wait __P((struct fxp_softc *));
|
|
static int fxp_intr __P((void *));
|
|
static void fxp_start __P((struct ifnet *));
|
|
static int fxp_ioctl __P((struct ifnet *, u_long, caddr_t));
|
|
static void fxp_init __P((void *));
|
|
static void fxp_stop __P((struct fxp_softc *));
|
|
static void fxp_watchdog __P((struct ifnet *));
|
|
static int fxp_add_rfabuf __P((struct fxp_softc *, struct fxp_rxdesc *));
|
|
static int fxp_mdi_read __P((struct device *, int, int));
|
|
static void fxp_statchg __P((struct device *));
|
|
static void fxp_mdi_write __P((struct device *, int, int, int));
|
|
static void fxp_read_eeprom __P((struct fxp_softc *, u_int16_t *,
|
|
int, int));
|
|
static void fxp_get_info __P((struct fxp_softc *, u_int8_t *));
|
|
void fxp_tick __P((void *));
|
|
static void fxp_mc_setup __P((struct fxp_softc *));
|
|
|
|
struct fxp_phytype {
|
|
int fp_phy; /* type of PHY, -1 for MII at the end. */
|
|
void (*fp_init) __P((struct fxp_softc *));
|
|
} fxp_phytype_table[] = {
|
|
{ FXP_PHY_80C24, fxp_80c24_initmedia },
|
|
{ -1, fxp_mii_initmedia },
|
|
};
|
|
|
|
/*
|
|
* Set initial transmit threshold at 64 (512 bytes). This is
|
|
* increased by 64 (512 bytes) at a time, to maximum of 192
|
|
* (1536 bytes), if an underrun occurs.
|
|
*/
|
|
static int tx_threshold = 64;
|
|
|
|
/*
|
|
* Wait for the previous command to be accepted (but not necessarily
|
|
* completed).
|
|
*/
|
|
static inline void
|
|
fxp_scb_wait(sc)
|
|
struct fxp_softc *sc;
|
|
{
|
|
int i = 10000;
|
|
|
|
while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i)
|
|
DELAY(1);
|
|
if (i == 0)
|
|
printf("%s: WARNING: SCB timed out!\n", sc->sc_dev.dv_xname);
|
|
}
|
|
|
|
static int fxp_match __P((struct device *, struct cfdata *, void *));
|
|
static void fxp_attach __P((struct device *, struct device *, void *));
|
|
|
|
static void fxp_shutdown __P((void *));
|
|
|
|
struct cfattach fxp_ca = {
|
|
sizeof(struct fxp_softc), fxp_match, fxp_attach
|
|
};
|
|
|
|
/*
|
|
* Check if a device is an 82557.
|
|
*/
|
|
static int
|
|
fxp_match(parent, match, aux)
|
|
struct device *parent;
|
|
struct cfdata *match;
|
|
void *aux;
|
|
{
|
|
struct pci_attach_args *pa = aux;
|
|
|
|
if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL)
|
|
return (0);
|
|
|
|
switch (PCI_PRODUCT(pa->pa_id)) {
|
|
case PCI_PRODUCT_INTEL_82557:
|
|
return (1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
fxp_attach(parent, self, aux)
|
|
struct device *parent, *self;
|
|
void *aux;
|
|
{
|
|
struct fxp_softc *sc = (struct fxp_softc *)self;
|
|
struct pci_attach_args *pa = aux;
|
|
pci_chipset_tag_t pc = pa->pa_pc;
|
|
pci_intr_handle_t ih;
|
|
const char *intrstr = NULL;
|
|
u_int8_t enaddr[6];
|
|
struct ifnet *ifp;
|
|
bus_space_tag_t iot, memt;
|
|
bus_space_handle_t ioh, memh;
|
|
bus_dma_segment_t seg;
|
|
int ioh_valid, memh_valid;
|
|
bus_addr_t addr;
|
|
bus_size_t size;
|
|
int flags, rseg, i, error, attach_stage;
|
|
struct fxp_phytype *fp;
|
|
|
|
/*
|
|
* Map control/status registers.
|
|
*/
|
|
ioh_valid = (pci_mapreg_map(pa, FXP_PCI_IOBA,
|
|
PCI_MAPREG_TYPE_IO, 0,
|
|
&iot, &ioh, NULL, NULL) == 0);
|
|
|
|
/*
|
|
* Version 2.1 of the PCI spec, page 196, "Address Maps":
|
|
*
|
|
* Prefetchable
|
|
*
|
|
* Set to one if there are no side effects on reads, the
|
|
* device returns all bytes regardless of the byte enables,
|
|
* and host bridges can merge processor writes into this
|
|
* range without causing errors. Bit must be set to zero
|
|
* otherwise.
|
|
*
|
|
* The 82557 incorrectly sets the "prefetchable" bit, resulting
|
|
* in errors on systems which will do merged reads and writes.
|
|
* These errors manifest themselves as all-bits-set when reading
|
|
* from the EEPROM or other < 4 byte registers.
|
|
*
|
|
* We must work around this problem by always forcing the mapping
|
|
* for memory space to be uncacheable. On systems which cannot
|
|
* create an uncacheable mapping (because the firmware mapped it
|
|
* into only cacheable/prefetchable space due to the "prefetchable"
|
|
* bit), we can fall back onto i/o mapped access.
|
|
*/
|
|
memh_valid = 0;
|
|
memt = pa->pa_memt;
|
|
if (((pa->pa_flags & PCI_FLAGS_MEM_ENABLED) != 0) &&
|
|
pci_mapreg_info(pa->pa_pc, pa->pa_tag, FXP_PCI_MMBA,
|
|
PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT,
|
|
&addr, &size, &flags) == 0) {
|
|
flags &= ~BUS_SPACE_MAP_CACHEABLE;
|
|
if (bus_space_map(memt, addr, size, flags, &memh) == 0)
|
|
memh_valid = 1;
|
|
}
|
|
|
|
if (memh_valid) {
|
|
sc->sc_st = memt;
|
|
sc->sc_sh = memh;
|
|
} else if (ioh_valid) {
|
|
sc->sc_st = iot;
|
|
sc->sc_sh = ioh;
|
|
} else {
|
|
printf(": unable to map device registers\n");
|
|
return;
|
|
}
|
|
|
|
sc->sc_dmat = pa->pa_dmat;
|
|
|
|
printf(": Intel EtherExpress Pro 10+/100B Ethernet\n");
|
|
|
|
/*
|
|
* Allocate our interrupt.
|
|
*/
|
|
if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin,
|
|
pa->pa_intrline, &ih)) {
|
|
printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname);
|
|
return;
|
|
}
|
|
intrstr = pci_intr_string(pc, ih);
|
|
sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, fxp_intr, sc);
|
|
if (sc->sc_ih == NULL) {
|
|
printf("%s: couldn't establish interrupt",
|
|
sc->sc_dev.dv_xname);
|
|
if (intrstr != NULL)
|
|
printf(" at %s", intrstr);
|
|
printf("\n");
|
|
return;
|
|
}
|
|
printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr);
|
|
|
|
attach_stage = 0;
|
|
|
|
/*
|
|
* Allocate the control data, and create and load the DMA
|
|
* map for it.
|
|
*/
|
|
if ((error = bus_dmamem_alloc(sc->sc_dmat,
|
|
sizeof(struct fxp_control_data), NBPG, 0, &seg, 1, &rseg,
|
|
BUS_DMA_NOWAIT)) != 0) {
|
|
printf("%s: can't allocate control data, error = %d\n",
|
|
sc->sc_dev.dv_xname, error);
|
|
goto fail;
|
|
}
|
|
|
|
attach_stage = 1;
|
|
|
|
if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg,
|
|
sizeof(struct fxp_control_data), (caddr_t *)&sc->control_data,
|
|
BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) {
|
|
printf("%s: can't map control data, error = %d\n",
|
|
sc->sc_dev.dv_xname, error);
|
|
goto fail;
|
|
}
|
|
bzero(sc->control_data, sizeof(struct fxp_control_data));
|
|
|
|
attach_stage = 2;
|
|
|
|
if ((error = bus_dmamap_create(sc->sc_dmat,
|
|
sizeof(struct fxp_control_data), 1,
|
|
sizeof(struct fxp_control_data), 0, BUS_DMA_NOWAIT,
|
|
&sc->sc_dmamap)) != 0) {
|
|
printf("%s: can't create control data DMA map, error = %d\n",
|
|
sc->sc_dev.dv_xname, error);
|
|
goto fail;
|
|
}
|
|
|
|
attach_stage = 3;
|
|
|
|
if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap,
|
|
sc->control_data, sizeof(struct fxp_control_data), NULL,
|
|
BUS_DMA_NOWAIT)) != 0) {
|
|
printf("%s: can't load control data DMA map, error = %d\n",
|
|
sc->sc_dev.dv_xname, error);
|
|
goto fail;
|
|
}
|
|
|
|
attach_stage = 4;
|
|
|
|
/*
|
|
* Create the transmit buffer DMA maps.
|
|
*/
|
|
for (i = 0; i < FXP_NTXCB; i++) {
|
|
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
|
|
FXP_NTXSEG, MCLBYTES, 0, BUS_DMA_NOWAIT,
|
|
&sc->sc_tx_dmamaps[i])) != 0) {
|
|
printf("%s: can't create tx DMA map %d, error = %d\n",
|
|
sc->sc_dev.dv_xname, i, error);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
attach_stage = 5;
|
|
|
|
/*
|
|
* Create the receive buffer DMA maps.
|
|
*/
|
|
for (i = 0; i < FXP_NRFABUFS; i++) {
|
|
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
|
|
MCLBYTES, 0, BUS_DMA_NOWAIT, &sc->sc_rx_dmamaps[i])) != 0) {
|
|
printf("%s: can't create rx DMA map %d, error = %d\n",
|
|
sc->sc_dev.dv_xname, i, error);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
attach_stage = 6;
|
|
|
|
/*
|
|
* Pre-allocate the receive buffer descriptors and the buffers
|
|
* themselves.
|
|
*/
|
|
sc->sc_rxdescs = malloc(sizeof(struct fxp_rxdesc) * FXP_NRFABUFS,
|
|
M_DEVBUF, M_NOWAIT);
|
|
if (sc->sc_rxdescs == NULL) {
|
|
printf("%s: can't allocate rx buffer descriptors\n",
|
|
sc->sc_dev.dv_xname);
|
|
goto fail;
|
|
}
|
|
bzero(sc->sc_rxdescs, sizeof(struct fxp_rxdesc) * FXP_NRFABUFS);
|
|
|
|
attach_stage = 7;
|
|
|
|
for (i = 0; i < FXP_NRFABUFS; i++) {
|
|
sc->sc_rxdescs[i].fr_dmamap = sc->sc_rx_dmamaps[i];
|
|
if (fxp_add_rfabuf(sc, &sc->sc_rxdescs[i]) != 0) {
|
|
printf("%s: can't allocate or map rx buffers\n",
|
|
sc->sc_dev.dv_xname);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
attach_stage = 8;
|
|
|
|
/* Initialize MAC address and media structures. */
|
|
fxp_get_info(sc, enaddr);
|
|
|
|
printf("%s: Ethernet address %s%s\n", sc->sc_dev.dv_xname,
|
|
ether_sprintf(enaddr), sc->phy_10Mbps_only ? ", 10Mbps" : "");
|
|
|
|
ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
/*
|
|
* Get info about our media interface, and initialize it. Note
|
|
* the table terminates itself with a phy of -1, indicating
|
|
* that we're using MII.
|
|
*/
|
|
for (fp = fxp_phytype_table; fp->fp_phy != -1; fp++)
|
|
if (fp->fp_phy == sc->phy_primary_device)
|
|
break;
|
|
(*fp->fp_init)(sc);
|
|
|
|
bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
|
|
ifp->if_softc = sc;
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
ifp->if_ioctl = fxp_ioctl;
|
|
ifp->if_start = fxp_start;
|
|
ifp->if_watchdog = fxp_watchdog;
|
|
|
|
/*
|
|
* Attach the interface.
|
|
*/
|
|
if_attach(ifp);
|
|
/*
|
|
* Let the system queue as many packets as we have TX descriptors.
|
|
*/
|
|
ifp->if_snd.ifq_maxlen = FXP_NTXCB;
|
|
ether_ifattach(ifp, enaddr);
|
|
#if NBPFILTER > 0
|
|
bpfattach(&sc->sc_ethercom.ec_if.if_bpf, ifp, DLT_EN10MB,
|
|
sizeof(struct ether_header));
|
|
#endif
|
|
|
|
/*
|
|
* Add shutdown hook so that DMA is disabled prior to reboot. Not
|
|
* doing do could allow DMA to corrupt kernel memory during the
|
|
* reboot before the driver initializes.
|
|
*/
|
|
shutdownhook_establish(fxp_shutdown, sc);
|
|
return;
|
|
|
|
fail:
|
|
/*
|
|
* Free any resources we've allocated during the failed attach
|
|
* attempt. Do this in reverse order and fall though.
|
|
*/
|
|
switch (attach_stage) {
|
|
case 8:
|
|
{
|
|
struct fxp_rxdesc *rxd;
|
|
|
|
for (i = 0; i < FXP_NRFABUFS; i++) {
|
|
rxd = &sc->sc_rxdescs[i];
|
|
if (rxd->fr_mbhead != NULL) {
|
|
bus_dmamap_unload(sc->sc_dmat, rxd->fr_dmamap);
|
|
m_freem(rxd->fr_mbhead);
|
|
}
|
|
}
|
|
}
|
|
/* FALLTHROUGH */
|
|
|
|
case 7:
|
|
free(sc->sc_rxdescs, M_DEVBUF);
|
|
/* FALLTHROUGH */
|
|
|
|
case 6:
|
|
for (i = 0; i < FXP_NRFABUFS; i++)
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_rx_dmamaps[i]);
|
|
/* FALLTHROUGH */
|
|
|
|
case 5:
|
|
for (i = 0; i < FXP_NTXCB; i++)
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_tx_dmamaps[i]);
|
|
/* FALLTHROUGH */
|
|
|
|
case 4:
|
|
bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap);
|
|
/* FALLTHROUGH */
|
|
|
|
case 3:
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
|
|
/* FALLTHROUGH */
|
|
|
|
case 2:
|
|
bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->control_data,
|
|
sizeof(struct fxp_control_data));
|
|
/* FALLTHROUGH */
|
|
|
|
case 1:
|
|
bus_dmamem_free(sc->sc_dmat, &seg, rseg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
fxp_mii_initmedia(sc)
|
|
struct fxp_softc *sc;
|
|
{
|
|
|
|
sc->sc_mii.mii_ifp = &sc->sc_ethercom.ec_if;
|
|
sc->sc_mii.mii_readreg = fxp_mdi_read;
|
|
sc->sc_mii.mii_writereg = fxp_mdi_write;
|
|
sc->sc_mii.mii_statchg = fxp_statchg;
|
|
ifmedia_init(&sc->sc_mii.mii_media, 0, fxp_mii_mediachange,
|
|
fxp_mii_mediastatus);
|
|
mii_phy_probe(&sc->sc_dev, &sc->sc_mii, 0xffffffff);
|
|
if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
|
|
ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL);
|
|
ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE);
|
|
} else
|
|
ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO);
|
|
}
|
|
|
|
void
|
|
fxp_80c24_initmedia(sc)
|
|
struct fxp_softc *sc;
|
|
{
|
|
|
|
/*
|
|
* The Seeq 80c24 AutoDUPLEX(tm) Ethernet Interface Adapter
|
|
* doesn't have a programming interface of any sort. The
|
|
* media is sensed automatically based on how the link partner
|
|
* is configured. This is, in essence, manual configuration.
|
|
*/
|
|
printf("%s: Seeq 80c24 AutoDUPLEX media interface present\n",
|
|
sc->sc_dev.dv_xname);
|
|
ifmedia_init(&sc->sc_mii.mii_media, 0, fxp_80c24_mediachange,
|
|
fxp_80c24_mediastatus);
|
|
ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL, 0, NULL);
|
|
ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL);
|
|
}
|
|
|
|
/*
|
|
* Device shutdown routine. Called at system shutdown after sync. The
|
|
* main purpose of this routine is to shut off receiver DMA so that
|
|
* kernel memory doesn't get clobbered during warmboot.
|
|
*/
|
|
static void
|
|
fxp_shutdown(sc)
|
|
void *sc;
|
|
{
|
|
|
|
fxp_stop((struct fxp_softc *) sc);
|
|
}
|
|
|
|
/*
|
|
* Initialize the interface media.
|
|
*/
|
|
static void
|
|
fxp_get_info(sc, enaddr)
|
|
struct fxp_softc *sc;
|
|
u_int8_t *enaddr;
|
|
{
|
|
u_int16_t data, myea[3];
|
|
|
|
/*
|
|
* Reset to a stable state.
|
|
*/
|
|
CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET);
|
|
DELAY(10);
|
|
|
|
/*
|
|
* Get info about the primary PHY
|
|
*/
|
|
fxp_read_eeprom(sc, &data, 6, 1);
|
|
sc->phy_primary_addr = data & 0xff;
|
|
sc->phy_primary_device = (data >> 8) & 0x3f;
|
|
sc->phy_10Mbps_only = data >> 15;
|
|
|
|
/*
|
|
* Read MAC address.
|
|
*/
|
|
fxp_read_eeprom(sc, myea, 0, 3);
|
|
bcopy(myea, enaddr, ETHER_ADDR_LEN);
|
|
}
|
|
|
|
/*
|
|
* Read from the serial EEPROM. Basically, you manually shift in
|
|
* the read opcode (one bit at a time) and then shift in the address,
|
|
* and then you shift out the data (all of this one bit at a time).
|
|
* The word size is 16 bits, so you have to provide the address for
|
|
* every 16 bits of data.
|
|
*/
|
|
static void
|
|
fxp_read_eeprom(sc, data, offset, words)
|
|
struct fxp_softc *sc;
|
|
u_int16_t *data;
|
|
int offset;
|
|
int words;
|
|
{
|
|
u_int16_t reg;
|
|
int i, x;
|
|
|
|
for (i = 0; i < words; i++) {
|
|
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
|
|
/*
|
|
* Shift in read opcode.
|
|
*/
|
|
for (x = 3; x > 0; x--) {
|
|
if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) {
|
|
reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI;
|
|
} else {
|
|
reg = FXP_EEPROM_EECS;
|
|
}
|
|
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
|
|
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL,
|
|
reg | FXP_EEPROM_EESK);
|
|
DELAY(1);
|
|
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
|
|
DELAY(1);
|
|
}
|
|
/*
|
|
* Shift in address.
|
|
*/
|
|
for (x = 6; x > 0; x--) {
|
|
if ((i + offset) & (1 << (x - 1))) {
|
|
reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI;
|
|
} else {
|
|
reg = FXP_EEPROM_EECS;
|
|
}
|
|
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
|
|
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL,
|
|
reg | FXP_EEPROM_EESK);
|
|
DELAY(1);
|
|
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
|
|
DELAY(1);
|
|
}
|
|
reg = FXP_EEPROM_EECS;
|
|
data[i] = 0;
|
|
/*
|
|
* Shift out data.
|
|
*/
|
|
for (x = 16; x > 0; x--) {
|
|
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL,
|
|
reg | FXP_EEPROM_EESK);
|
|
DELAY(1);
|
|
if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) &
|
|
FXP_EEPROM_EEDO)
|
|
data[i] |= (1 << (x - 1));
|
|
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
|
|
DELAY(1);
|
|
}
|
|
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
|
|
DELAY(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Start packet transmission on the interface.
|
|
*/
|
|
static void
|
|
fxp_start(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
struct fxp_softc *sc = ifp->if_softc;
|
|
struct fxp_cb_tx *txp;
|
|
bus_dmamap_t dmamap;
|
|
int old_queued;
|
|
|
|
/*
|
|
* See if we need to suspend xmit until the multicast filter
|
|
* has been reprogrammed (which can only be done at the head
|
|
* of the command chain).
|
|
*/
|
|
if (sc->need_mcsetup || (old_queued = sc->tx_queued) >= FXP_NTXCB) {
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We're finished if there is nothing more to add to the list or if
|
|
* we're all filled up with buffers to transmit.
|
|
*/
|
|
while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB) {
|
|
struct mbuf *mb_head;
|
|
int segment, error;
|
|
|
|
/*
|
|
* Grab a packet to transmit.
|
|
*/
|
|
IF_DEQUEUE(&ifp->if_snd, mb_head);
|
|
|
|
/*
|
|
* Get pointer to next available tx desc.
|
|
*/
|
|
txp = sc->cbl_last->cb_soft.next;
|
|
dmamap = txp->cb_soft.dmamap;
|
|
|
|
/*
|
|
* Go through each of the mbufs in the chain and initialize
|
|
* the transmit buffer descriptors with the physical address
|
|
* and size of the mbuf.
|
|
*/
|
|
tbdinit:
|
|
error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap,
|
|
mb_head, BUS_DMA_NOWAIT);
|
|
switch (error) {
|
|
case 0:
|
|
/* Success. */
|
|
break;
|
|
|
|
case EFBIG:
|
|
{
|
|
struct mbuf *mn;
|
|
|
|
/*
|
|
* We ran out of segments. We have to recopy this
|
|
* mbuf chain first. Bail out if we can't get the
|
|
* new buffers.
|
|
*/
|
|
printf("%s: too many segments, ", sc->sc_dev.dv_xname);
|
|
|
|
MGETHDR(mn, M_DONTWAIT, MT_DATA);
|
|
if (mn == NULL) {
|
|
m_freem(mb_head);
|
|
printf("aborting\n");
|
|
goto out;
|
|
}
|
|
if (mb_head->m_pkthdr.len > MHLEN) {
|
|
MCLGET(mn, M_DONTWAIT);
|
|
if ((mn->m_flags & M_EXT) == 0) {
|
|
m_freem(mn);
|
|
m_freem(mb_head);
|
|
printf("aborting\n");
|
|
goto out;
|
|
}
|
|
}
|
|
m_copydata(mb_head, 0, mb_head->m_pkthdr.len,
|
|
mtod(mn, caddr_t));
|
|
mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len;
|
|
m_freem(mb_head);
|
|
mb_head = mn;
|
|
printf("retrying\n");
|
|
goto tbdinit;
|
|
}
|
|
|
|
default:
|
|
/*
|
|
* Some other problem; report it.
|
|
*/
|
|
printf("%s: can't load mbuf chain, error = %d\n",
|
|
sc->sc_dev.dv_xname, error);
|
|
m_freem(mb_head);
|
|
goto out;
|
|
}
|
|
|
|
for (segment = 0; segment < dmamap->dm_nsegs; segment++) {
|
|
txp->tbd[segment].tb_addr =
|
|
dmamap->dm_segs[segment].ds_addr;
|
|
txp->tbd[segment].tb_size =
|
|
dmamap->dm_segs[segment].ds_len;
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize,
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
txp->tbd_number = dmamap->dm_nsegs;
|
|
txp->cb_soft.mb_head = mb_head;
|
|
txp->cb_status = 0;
|
|
txp->cb_command =
|
|
FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S;
|
|
txp->tx_threshold = tx_threshold;
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
FXP_TXDESCOFF(sc, txp), FXP_TXDESCSIZE,
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
/*
|
|
* Advance the end of list forward.
|
|
*/
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
FXP_TXDESCOFF(sc, sc->cbl_last), FXP_TXDESCSIZE,
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
|
sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S;
|
|
sc->cbl_last = txp;
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
FXP_TXDESCOFF(sc, sc->cbl_last), FXP_TXDESCSIZE,
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
/*
|
|
* Advance the beginning of the list forward if there are
|
|
* no other packets queued (when nothing is queued, cbl_first
|
|
* sits on the last TxCB that was sent out).
|
|
*/
|
|
if (sc->tx_queued == 0)
|
|
sc->cbl_first = txp;
|
|
|
|
sc->tx_queued++;
|
|
|
|
#if NBPFILTER > 0
|
|
/*
|
|
* Pass packet to bpf if there is a listener.
|
|
*/
|
|
if (ifp->if_bpf)
|
|
bpf_mtap(ifp->if_bpf, mb_head);
|
|
#endif
|
|
}
|
|
|
|
out:
|
|
/*
|
|
* We're finished. If we added to the list, issue a RESUME to get DMA
|
|
* going again if suspended.
|
|
*/
|
|
if (old_queued != sc->tx_queued) {
|
|
fxp_scb_wait(sc);
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME);
|
|
|
|
/*
|
|
* Set a 5 second timer just in case we don't hear from the
|
|
* card again.
|
|
*/
|
|
ifp->if_timer = 5;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process interface interrupts.
|
|
*/
|
|
static int
|
|
fxp_intr(arg)
|
|
void *arg;
|
|
{
|
|
struct fxp_softc *sc = arg;
|
|
struct ifnet *ifp = &sc->sc_if;
|
|
u_int8_t statack;
|
|
int claimed = 0;
|
|
|
|
while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) {
|
|
claimed = 1;
|
|
|
|
/*
|
|
* First ACK all the interrupts in this pass.
|
|
*/
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, statack);
|
|
|
|
/*
|
|
* Process receiver interrupts. If a no-resource (RNR)
|
|
* condition exists, get whatever packets we can and
|
|
* re-start the receiver.
|
|
*/
|
|
if (statack & (FXP_SCB_STATACK_FR | FXP_SCB_STATACK_RNR)) {
|
|
struct fxp_rxdesc *rxd;
|
|
struct mbuf *m;
|
|
struct fxp_rfa *rfa;
|
|
bus_dmamap_t rxmap;
|
|
rcvloop:
|
|
rxd = sc->rfa_head;
|
|
rxmap = rxd->fr_dmamap;
|
|
m = rxd->fr_mbhead;
|
|
rfa = (struct fxp_rfa *)(m->m_ext.ext_buf +
|
|
RFA_ALIGNMENT_FUDGE);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, rxmap, 0,
|
|
rxmap->dm_mapsize,
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
|
|
|
if (rfa->rfa_status & FXP_RFA_STATUS_C) {
|
|
/*
|
|
* Remove first packet from the chain.
|
|
*/
|
|
sc->rfa_head = rxd->fr_next;
|
|
rxd->fr_next = NULL;
|
|
|
|
/*
|
|
* Add a new buffer to the receive chain.
|
|
* If this fails, the old buffer is recycled
|
|
* instead.
|
|
*/
|
|
if (fxp_add_rfabuf(sc, rxd) == 0) {
|
|
struct ether_header *eh;
|
|
u_int16_t total_len;
|
|
|
|
total_len = rfa->actual_size &
|
|
(MCLBYTES - 1);
|
|
if (total_len <
|
|
sizeof(struct ether_header)) {
|
|
m_freem(m);
|
|
goto rcvloop;
|
|
}
|
|
m->m_pkthdr.rcvif = ifp;
|
|
m->m_pkthdr.len = m->m_len =
|
|
total_len -
|
|
sizeof(struct ether_header);
|
|
eh = mtod(m, struct ether_header *);
|
|
#if NBPFILTER > 0
|
|
if (ifp->if_bpf) {
|
|
bpf_tap(ifp->if_bpf,
|
|
mtod(m, caddr_t),
|
|
total_len);
|
|
/*
|
|
* Only pass this packet up
|
|
* if it is for us.
|
|
*/
|
|
if ((ifp->if_flags &
|
|
IFF_PROMISC) &&
|
|
(rfa->rfa_status &
|
|
FXP_RFA_STATUS_IAMATCH) &&
|
|
(eh->ether_dhost[0] & 1)
|
|
== 0) {
|
|
m_freem(m);
|
|
goto rcvloop;
|
|
}
|
|
}
|
|
#endif /* NBPFILTER > 0 */
|
|
m->m_data +=
|
|
sizeof(struct ether_header);
|
|
ether_input(ifp, eh, m);
|
|
}
|
|
goto rcvloop;
|
|
}
|
|
if (statack & FXP_SCB_STATACK_RNR) {
|
|
fxp_scb_wait(sc);
|
|
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL,
|
|
rxmap->dm_segs[0].ds_addr +
|
|
RFA_ALIGNMENT_FUDGE);
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND,
|
|
FXP_SCB_COMMAND_RU_START);
|
|
}
|
|
}
|
|
/*
|
|
* Free any finished transmit mbuf chains.
|
|
*/
|
|
if (statack & FXP_SCB_STATACK_CNA) {
|
|
struct fxp_cb_tx *txp;
|
|
bus_dmamap_t txmap;
|
|
|
|
for (txp = sc->cbl_first; sc->tx_queued;
|
|
txp = txp->cb_soft.next) {
|
|
bus_dmamap_sync(sc->sc_dmat,
|
|
sc->sc_dmamap, FXP_TXDESCOFF(sc, txp),
|
|
FXP_TXDESCSIZE,
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
|
if ((txp->cb_status & FXP_CB_STATUS_C) == 0)
|
|
break;
|
|
if (txp->cb_soft.mb_head != NULL) {
|
|
txmap = txp->cb_soft.dmamap;
|
|
bus_dmamap_sync(sc->sc_dmat, txmap,
|
|
0, txmap->dm_mapsize,
|
|
BUS_DMASYNC_POSTWRITE);
|
|
bus_dmamap_unload(sc->sc_dmat, txmap);
|
|
m_freem(txp->cb_soft.mb_head);
|
|
txp->cb_soft.mb_head = NULL;
|
|
}
|
|
sc->tx_queued--;
|
|
}
|
|
sc->cbl_first = txp;
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
if (sc->tx_queued == 0) {
|
|
ifp->if_timer = 0;
|
|
if (sc->need_mcsetup)
|
|
fxp_mc_setup(sc);
|
|
}
|
|
/*
|
|
* Try to start more packets transmitting.
|
|
*/
|
|
if (ifp->if_snd.ifq_head != NULL)
|
|
fxp_start(ifp);
|
|
}
|
|
}
|
|
return (claimed);
|
|
}
|
|
|
|
/*
|
|
* Update packet in/out/collision statistics. The i82557 doesn't
|
|
* allow you to access these counters without doing a fairly
|
|
* expensive DMA to get _all_ of the statistics it maintains, so
|
|
* we do this operation here only once per second. The statistics
|
|
* counters in the kernel are updated from the previous dump-stats
|
|
* DMA and then a new dump-stats DMA is started. The on-chip
|
|
* counters are zeroed when the DMA completes. If we can't start
|
|
* the DMA immediately, we don't wait - we just prepare to read
|
|
* them again next time.
|
|
*/
|
|
void
|
|
fxp_tick(arg)
|
|
void *arg;
|
|
{
|
|
struct fxp_softc *sc = arg;
|
|
struct ifnet *ifp = &sc->sc_if;
|
|
struct fxp_stats *sp = &sc->control_data->fcd_stats;
|
|
int s = splnet();
|
|
|
|
ifp->if_opackets += sp->tx_good;
|
|
ifp->if_collisions += sp->tx_total_collisions;
|
|
if (sp->rx_good) {
|
|
ifp->if_ipackets += sp->rx_good;
|
|
sc->rx_idle_secs = 0;
|
|
} else {
|
|
sc->rx_idle_secs++;
|
|
}
|
|
ifp->if_ierrors +=
|
|
sp->rx_crc_errors +
|
|
sp->rx_alignment_errors +
|
|
sp->rx_rnr_errors +
|
|
sp->rx_overrun_errors;
|
|
/*
|
|
* If any transmit underruns occured, bump up the transmit
|
|
* threshold by another 512 bytes (64 * 8).
|
|
*/
|
|
if (sp->tx_underruns) {
|
|
ifp->if_oerrors += sp->tx_underruns;
|
|
if (tx_threshold < 192)
|
|
tx_threshold += 64;
|
|
}
|
|
|
|
/*
|
|
* If we haven't received any packets in FXP_MAC_RX_IDLE seconds,
|
|
* then assume the receiver has locked up and attempt to clear
|
|
* the condition by reprogramming the multicast filter. This is
|
|
* a work-around for a bug in the 82557 where the receiver locks
|
|
* up if it gets certain types of garbage in the syncronization
|
|
* bits prior to the packet header. This bug is supposed to only
|
|
* occur in 10Mbps mode, but has been seen to occur in 100Mbps
|
|
* mode as well (perhaps due to a 10/100 speed transition).
|
|
*/
|
|
if (sc->rx_idle_secs > FXP_MAX_RX_IDLE) {
|
|
sc->rx_idle_secs = 0;
|
|
fxp_mc_setup(sc);
|
|
}
|
|
/*
|
|
* If there is no pending command, start another stats
|
|
* dump. Otherwise punt for now.
|
|
*/
|
|
if (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) == 0) {
|
|
/*
|
|
* Start another stats dump.
|
|
*/
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND,
|
|
FXP_SCB_COMMAND_CU_DUMPRESET);
|
|
} else {
|
|
/*
|
|
* A previous command is still waiting to be accepted.
|
|
* Just zero our copy of the stats and wait for the
|
|
* next timer event to update them.
|
|
*/
|
|
sp->tx_good = 0;
|
|
sp->tx_underruns = 0;
|
|
sp->tx_total_collisions = 0;
|
|
|
|
sp->rx_good = 0;
|
|
sp->rx_crc_errors = 0;
|
|
sp->rx_alignment_errors = 0;
|
|
sp->rx_rnr_errors = 0;
|
|
sp->rx_overrun_errors = 0;
|
|
}
|
|
|
|
/* Tick the MII clock. */
|
|
mii_tick(&sc->sc_mii);
|
|
splx(s);
|
|
|
|
/*
|
|
* Schedule another timeout one second from now.
|
|
*/
|
|
timeout(fxp_tick, sc, hz);
|
|
}
|
|
|
|
/*
|
|
* Stop the interface. Cancels the statistics updater and resets
|
|
* the interface.
|
|
*/
|
|
static void
|
|
fxp_stop(sc)
|
|
struct fxp_softc *sc;
|
|
{
|
|
struct ifnet *ifp = &sc->sc_if;
|
|
struct fxp_rxdesc *rxd;
|
|
struct fxp_cb_tx *txp;
|
|
int i;
|
|
|
|
/*
|
|
* Cancel stats updater.
|
|
*/
|
|
untimeout(fxp_tick, sc);
|
|
|
|
/*
|
|
* Issue software reset
|
|
*/
|
|
CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET);
|
|
DELAY(10);
|
|
|
|
/*
|
|
* Release any xmit buffers.
|
|
*/
|
|
for (txp = sc->control_data->fcd_txcbs, i = 0; i < FXP_NTXCB; i++) {
|
|
if (txp[i].cb_soft.mb_head != NULL) {
|
|
bus_dmamap_unload(sc->sc_dmat, txp[i].cb_soft.dmamap);
|
|
m_freem(txp[i].cb_soft.mb_head);
|
|
txp[i].cb_soft.mb_head = NULL;
|
|
}
|
|
}
|
|
sc->tx_queued = 0;
|
|
|
|
/*
|
|
* Free all the receive buffers then reallocate/reinitialize
|
|
*/
|
|
sc->rfa_head = NULL;
|
|
sc->rfa_tail = NULL;
|
|
for (i = 0; i < FXP_NRFABUFS; i++) {
|
|
rxd = &sc->sc_rxdescs[i];
|
|
if (rxd->fr_mbhead != NULL) {
|
|
bus_dmamap_unload(sc->sc_dmat, rxd->fr_dmamap);
|
|
m_freem(rxd->fr_mbhead);
|
|
rxd->fr_mbhead = NULL;
|
|
}
|
|
if (fxp_add_rfabuf(sc, rxd) != 0) {
|
|
/*
|
|
* This "can't happen" - we're at splnet()
|
|
* and we just freed the buffer we need
|
|
* above.
|
|
*/
|
|
panic("fxp_stop: no buffers!");
|
|
}
|
|
}
|
|
|
|
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
|
|
ifp->if_timer = 0;
|
|
}
|
|
|
|
/*
|
|
* Watchdog/transmission transmit timeout handler. Called when a
|
|
* transmission is started on the interface, but no interrupt is
|
|
* received before the timeout. This usually indicates that the
|
|
* card has wedged for some reason.
|
|
*/
|
|
static void
|
|
fxp_watchdog(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
struct fxp_softc *sc = ifp->if_softc;
|
|
|
|
printf("%s: device timeout\n", sc->sc_dev.dv_xname);
|
|
ifp->if_oerrors++;
|
|
|
|
fxp_init(sc);
|
|
}
|
|
|
|
static void
|
|
fxp_init(xsc)
|
|
void *xsc;
|
|
{
|
|
struct fxp_softc *sc = xsc;
|
|
struct ifnet *ifp = &sc->sc_if;
|
|
struct fxp_cb_config *cbp;
|
|
struct fxp_cb_ias *cb_ias;
|
|
struct fxp_cb_tx *txp;
|
|
int i, s, prm;
|
|
|
|
s = splnet();
|
|
/*
|
|
* Cancel any pending I/O
|
|
*/
|
|
fxp_stop(sc);
|
|
|
|
prm = (ifp->if_flags & IFF_PROMISC) ? 1 : 0;
|
|
sc->promisc_mode = prm;
|
|
|
|
/*
|
|
* Initialize base of CBL and RFA memory. Loading with zero
|
|
* sets it up for regular linear addressing.
|
|
*/
|
|
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 0);
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_BASE);
|
|
|
|
fxp_scb_wait(sc);
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_BASE);
|
|
|
|
/*
|
|
* Initialize base of dump-stats buffer.
|
|
*/
|
|
fxp_scb_wait(sc);
|
|
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL,
|
|
sc->sc_cddma + FXP_CDOFF(fcd_stats));
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_DUMP_ADR);
|
|
|
|
/*
|
|
* We temporarily use memory that contains the TxCB list to
|
|
* construct the config CB. The TxCB list memory is rebuilt
|
|
* later.
|
|
*/
|
|
cbp = (struct fxp_cb_config *) sc->control_data->fcd_txcbs;
|
|
|
|
/*
|
|
* This bcopy is kind of disgusting, but there are a bunch of must be
|
|
* zero and must be one bits in this structure and this is the easiest
|
|
* way to initialize them all to proper values.
|
|
*/
|
|
bcopy(fxp_cb_config_template, (void *)&cbp->cb_status,
|
|
sizeof(fxp_cb_config_template));
|
|
|
|
cbp->cb_status = 0;
|
|
cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL;
|
|
cbp->link_addr = -1; /* (no) next command */
|
|
cbp->byte_count = 22; /* (22) bytes to config */
|
|
cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */
|
|
cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */
|
|
cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */
|
|
cbp->rx_dma_bytecount = 0; /* (no) rx DMA max */
|
|
cbp->tx_dma_bytecount = 0; /* (no) tx DMA max */
|
|
cbp->dma_bce = 0; /* (disable) dma max counters */
|
|
cbp->late_scb = 0; /* (don't) defer SCB update */
|
|
cbp->tno_int = 0; /* (disable) tx not okay interrupt */
|
|
cbp->ci_int = 0; /* interrupt on CU not active */
|
|
cbp->save_bf = prm; /* save bad frames */
|
|
cbp->disc_short_rx = !prm; /* discard short packets */
|
|
cbp->underrun_retry = 1; /* retry mode (1) on DMA underrun */
|
|
cbp->mediatype = !sc->phy_10Mbps_only; /* interface mode */
|
|
cbp->nsai = 1; /* (don't) disable source addr insert */
|
|
cbp->preamble_length = 2; /* (7 byte) preamble */
|
|
cbp->loopback = 0; /* (don't) loopback */
|
|
cbp->linear_priority = 0; /* (normal CSMA/CD operation) */
|
|
cbp->linear_pri_mode = 0; /* (wait after xmit only) */
|
|
cbp->interfrm_spacing = 6; /* (96 bits of) interframe spacing */
|
|
cbp->promiscuous = prm; /* promiscuous mode */
|
|
cbp->bcast_disable = 0; /* (don't) disable broadcasts */
|
|
cbp->crscdt = 0; /* (CRS only) */
|
|
cbp->stripping = !prm; /* truncate rx packet to byte count */
|
|
cbp->padding = 1; /* (do) pad short tx packets */
|
|
cbp->rcv_crc_xfer = 0; /* (don't) xfer CRC to host */
|
|
cbp->force_fdx = 0; /* (don't) force full duplex */
|
|
cbp->fdx_pin_en = 1; /* (enable) FDX# pin */
|
|
cbp->multi_ia = 0; /* (don't) accept multiple IAs */
|
|
cbp->mc_all = sc->all_mcasts;/* accept all multicasts */
|
|
|
|
/*
|
|
* Start the config command/DMA.
|
|
*/
|
|
fxp_scb_wait(sc);
|
|
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL,
|
|
sc->sc_cddma + FXP_CDOFF(fcd_txcbs[0].cb_status));
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
|
|
/* ...and wait for it to complete. */
|
|
while (!(cbp->cb_status & FXP_CB_STATUS_C));
|
|
|
|
/*
|
|
* Now initialize the station address. Temporarily use the TxCB
|
|
* memory area like we did above for the config CB.
|
|
*/
|
|
cb_ias = (struct fxp_cb_ias *) sc->control_data->fcd_txcbs;
|
|
cb_ias->cb_status = 0;
|
|
cb_ias->cb_command = FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL;
|
|
cb_ias->link_addr = -1;
|
|
bcopy(LLADDR(ifp->if_sadl), (void *)cb_ias->macaddr, 6);
|
|
|
|
/*
|
|
* Start the IAS (Individual Address Setup) command/DMA.
|
|
*/
|
|
fxp_scb_wait(sc);
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
|
|
/* ...and wait for it to complete. */
|
|
while (!(cb_ias->cb_status & FXP_CB_STATUS_C));
|
|
|
|
/*
|
|
* Initialize transmit control block (TxCB) list.
|
|
*/
|
|
|
|
txp = sc->control_data->fcd_txcbs;
|
|
bzero(txp, sizeof(sc->control_data->fcd_txcbs));
|
|
for (i = 0; i < FXP_NTXCB; i++) {
|
|
txp[i].cb_status = FXP_CB_STATUS_C | FXP_CB_STATUS_OK;
|
|
txp[i].cb_command = FXP_CB_COMMAND_NOP;
|
|
txp[i].link_addr = sc->sc_cddma +
|
|
FXP_CDOFF(fcd_txcbs[(i + 1) & FXP_TXCB_MASK].cb_status);
|
|
txp[i].tbd_array_addr = sc->sc_cddma +
|
|
FXP_CDOFF(fcd_txcbs[i].tbd[0]);
|
|
txp[i].cb_soft.dmamap = sc->sc_tx_dmamaps[i];
|
|
txp[i].cb_soft.next = &txp[(i + 1) & FXP_TXCB_MASK];
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
FXP_TXDESCOFF(sc, &txp[i]), FXP_TXDESCSIZE,
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
}
|
|
/*
|
|
* Set the suspend flag on the first TxCB and start the control
|
|
* unit. It will execute the NOP and then suspend.
|
|
*/
|
|
txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S;
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
FXP_TXDESCOFF(sc, txp), FXP_TXDESCSIZE,
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
sc->cbl_first = sc->cbl_last = txp;
|
|
sc->tx_queued = 1;
|
|
|
|
fxp_scb_wait(sc);
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
|
|
|
|
/*
|
|
* Initialize receiver buffer area - RFA.
|
|
*/
|
|
fxp_scb_wait(sc);
|
|
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL,
|
|
sc->rfa_head->fr_dmamap->dm_segs[0].ds_addr + RFA_ALIGNMENT_FUDGE);
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_START);
|
|
|
|
/*
|
|
* Set current media.
|
|
*/
|
|
mii_mediachg(&sc->sc_mii);
|
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
splx(s);
|
|
|
|
/*
|
|
* Start stats updater.
|
|
*/
|
|
timeout(fxp_tick, sc, hz);
|
|
}
|
|
|
|
/*
|
|
* Change media according to request.
|
|
*/
|
|
int
|
|
fxp_mii_mediachange(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
|
|
if (ifp->if_flags & IFF_UP)
|
|
fxp_init(ifp->if_softc);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Notify the world which media we're using.
|
|
*/
|
|
void
|
|
fxp_mii_mediastatus(ifp, ifmr)
|
|
struct ifnet *ifp;
|
|
struct ifmediareq *ifmr;
|
|
{
|
|
struct fxp_softc *sc = ifp->if_softc;
|
|
|
|
mii_pollstat(&sc->sc_mii);
|
|
ifmr->ifm_status = sc->sc_mii.mii_media_status;
|
|
ifmr->ifm_active = sc->sc_mii.mii_media_active;
|
|
}
|
|
|
|
int
|
|
fxp_80c24_mediachange(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
|
|
/* Nothing to do here. */
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
fxp_80c24_mediastatus(ifp, ifmr)
|
|
struct ifnet *ifp;
|
|
struct ifmediareq *ifmr;
|
|
{
|
|
struct fxp_softc *sc = ifp->if_softc;
|
|
|
|
/*
|
|
* Media is currently-selected media. We cannot determine
|
|
* the link status.
|
|
*/
|
|
ifmr->ifm_status = 0;
|
|
ifmr->ifm_active = sc->sc_mii.mii_media.ifm_cur->ifm_media;
|
|
}
|
|
|
|
/*
|
|
* Add a buffer to the end of the RFA buffer list.
|
|
* Return 0 if successful, 1 for failure. A failure results in
|
|
* adding the 'oldm' (if non-NULL) on to the end of the list -
|
|
* tossing out it's old contents and recycling it.
|
|
* The RFA struct is stuck at the beginning of mbuf cluster and the
|
|
* data pointer is fixed up to point just past it.
|
|
*/
|
|
static int
|
|
fxp_add_rfabuf(sc, rxd)
|
|
struct fxp_softc *sc;
|
|
struct fxp_rxdesc *rxd;
|
|
{
|
|
struct mbuf *m, *oldm;
|
|
struct fxp_rfa *rfa, *p_rfa;
|
|
bus_dmamap_t rxmap;
|
|
u_int32_t v;
|
|
int error, rval = 0;
|
|
|
|
oldm = rxd->fr_mbhead;
|
|
rxmap = rxd->fr_dmamap;
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
if (m != NULL) {
|
|
MCLGET(m, M_DONTWAIT);
|
|
if ((m->m_flags & M_EXT) == 0) {
|
|
m_freem(m);
|
|
if (oldm == NULL)
|
|
return 1;
|
|
m = oldm;
|
|
m->m_data = m->m_ext.ext_buf;
|
|
rval = 1;
|
|
}
|
|
} else {
|
|
if (oldm == NULL)
|
|
return 1;
|
|
m = oldm;
|
|
m->m_data = m->m_ext.ext_buf;
|
|
rval = 1;
|
|
}
|
|
|
|
rxd->fr_mbhead = m;
|
|
|
|
/*
|
|
* Setup the DMA map for this receive buffer.
|
|
*/
|
|
if (m != oldm) {
|
|
if (oldm != NULL)
|
|
bus_dmamap_unload(sc->sc_dmat, rxmap);
|
|
error = bus_dmamap_load(sc->sc_dmat, rxmap,
|
|
m->m_ext.ext_buf, MCLBYTES, NULL, BUS_DMA_NOWAIT);
|
|
if (error) {
|
|
printf("%s: can't load rx buffer, error = %d\n",
|
|
sc->sc_dev.dv_xname, error);
|
|
panic("fxp_add_rfabuf"); /* XXX */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Move the data pointer up so that the incoming data packet
|
|
* will be 32-bit aligned.
|
|
*/
|
|
m->m_data += RFA_ALIGNMENT_FUDGE;
|
|
|
|
/*
|
|
* Get a pointer to the base of the mbuf cluster and move
|
|
* data start past the RFA descriptor.
|
|
*/
|
|
rfa = mtod(m, struct fxp_rfa *);
|
|
m->m_data += sizeof(struct fxp_rfa);
|
|
rfa->size = MCLBYTES - sizeof(struct fxp_rfa) - RFA_ALIGNMENT_FUDGE;
|
|
|
|
/*
|
|
* Initialize the rest of the RFA.
|
|
*/
|
|
rfa->rfa_status = 0;
|
|
rfa->rfa_control = FXP_RFA_CONTROL_EL;
|
|
rfa->actual_size = 0;
|
|
|
|
/*
|
|
* Note that since the RFA is misaligned, we cannot store values
|
|
* directly. Instead, we must copy.
|
|
*/
|
|
v = -1;
|
|
memcpy((void *)&rfa->link_addr, &v, sizeof(v));
|
|
memcpy((void *)&rfa->rbd_addr, &v, sizeof(v));
|
|
|
|
/*
|
|
* If there are other buffers already on the list, attach this
|
|
* one to the end by fixing up the tail to point to this one.
|
|
*/
|
|
if (sc->rfa_head != NULL) {
|
|
p_rfa = (struct fxp_rfa *)
|
|
(sc->rfa_tail->fr_mbhead->m_ext.ext_buf +
|
|
RFA_ALIGNMENT_FUDGE);
|
|
sc->rfa_tail->fr_next = rxd;
|
|
v = rxmap->dm_segs[0].ds_addr + RFA_ALIGNMENT_FUDGE;
|
|
memcpy((void *)&p_rfa->link_addr, &v, sizeof(v));
|
|
p_rfa->rfa_control &= ~FXP_RFA_CONTROL_EL;
|
|
} else {
|
|
sc->rfa_head = rxd;
|
|
}
|
|
sc->rfa_tail = rxd;
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, rxmap, 0, rxmap->dm_mapsize,
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
return (rval);
|
|
}
|
|
|
|
static volatile int
|
|
fxp_mdi_read(self, phy, reg)
|
|
struct device *self;
|
|
int phy;
|
|
int reg;
|
|
{
|
|
struct fxp_softc *sc = (struct fxp_softc *)self;
|
|
int count = 10000;
|
|
int value;
|
|
|
|
CSR_WRITE_4(sc, FXP_CSR_MDICONTROL,
|
|
(FXP_MDI_READ << 26) | (reg << 16) | (phy << 21));
|
|
|
|
while (((value = CSR_READ_4(sc, FXP_CSR_MDICONTROL)) & 0x10000000) == 0
|
|
&& count--)
|
|
DELAY(10);
|
|
|
|
if (count <= 0)
|
|
printf("%s: fxp_mdi_read: timed out\n", sc->sc_dev.dv_xname);
|
|
|
|
return (value & 0xffff);
|
|
}
|
|
|
|
static void
|
|
fxp_statchg(self)
|
|
struct device *self;
|
|
{
|
|
|
|
/* XXX Update ifp->if_baudrate */
|
|
}
|
|
|
|
static void
|
|
fxp_mdi_write(self, phy, reg, value)
|
|
struct device *self;
|
|
int phy;
|
|
int reg;
|
|
int value;
|
|
{
|
|
struct fxp_softc *sc = (struct fxp_softc *)self;
|
|
int count = 10000;
|
|
|
|
CSR_WRITE_4(sc, FXP_CSR_MDICONTROL,
|
|
(FXP_MDI_WRITE << 26) | (reg << 16) | (phy << 21) |
|
|
(value & 0xffff));
|
|
|
|
while((CSR_READ_4(sc, FXP_CSR_MDICONTROL) & 0x10000000) == 0 &&
|
|
count--)
|
|
DELAY(10);
|
|
|
|
if (count <= 0)
|
|
printf("%s: fxp_mdi_write: timed out\n", sc->sc_dev.dv_xname);
|
|
}
|
|
|
|
static int
|
|
fxp_ioctl(ifp, command, data)
|
|
struct ifnet *ifp;
|
|
u_long command;
|
|
caddr_t data;
|
|
{
|
|
struct fxp_softc *sc = ifp->if_softc;
|
|
struct ifreq *ifr = (struct ifreq *)data;
|
|
struct ifaddr *ifa = (struct ifaddr *)data;
|
|
int s, error = 0;
|
|
|
|
s = splnet();
|
|
|
|
switch (command) {
|
|
case SIOCSIFADDR:
|
|
ifp->if_flags |= IFF_UP;
|
|
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
fxp_init(sc);
|
|
arp_ifinit(ifp, ifa);
|
|
break;
|
|
#endif
|
|
#ifdef NS
|
|
case AF_NS:
|
|
{
|
|
register struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
|
|
|
|
if (ns_nullhost(*ina))
|
|
ina->x_host = *(union ns_host *)
|
|
LLADDR(ifp->if_sadl);
|
|
else
|
|
bcopy(ina->x_host.c_host, LLADDR(ifp->if_sadl),
|
|
ifp->if_addrlen);
|
|
/* Set new address. */
|
|
fxp_init(sc);
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
fxp_init(sc);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SIOCSIFMTU:
|
|
if (ifr->ifr_mtu > ETHERMTU)
|
|
error = EINVAL;
|
|
else
|
|
ifp->if_mtu = ifr->ifr_mtu;
|
|
break;
|
|
|
|
case SIOCSIFFLAGS:
|
|
sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0;
|
|
|
|
/*
|
|
* If interface is marked up and not running, then start it.
|
|
* If it is marked down and running, stop it.
|
|
* XXX If it's up then re-initialize it. This is so flags
|
|
* such as IFF_PROMISC are handled.
|
|
*/
|
|
if (ifp->if_flags & IFF_UP) {
|
|
fxp_init(sc);
|
|
} else {
|
|
if (ifp->if_flags & IFF_RUNNING)
|
|
fxp_stop(sc);
|
|
}
|
|
break;
|
|
|
|
case SIOCADDMULTI:
|
|
case SIOCDELMULTI:
|
|
sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0;
|
|
error = (command == SIOCADDMULTI) ?
|
|
ether_addmulti(ifr, &sc->sc_ethercom) :
|
|
ether_delmulti(ifr, &sc->sc_ethercom);
|
|
|
|
if (error == ENETRESET) {
|
|
/*
|
|
* Multicast list has changed; set the hardware
|
|
* filter accordingly.
|
|
*/
|
|
if (!sc->all_mcasts)
|
|
fxp_mc_setup(sc);
|
|
/*
|
|
* fxp_mc_setup() can turn on all_mcasts if we run
|
|
* out of space, so check it again rather than else {}.
|
|
*/
|
|
if (sc->all_mcasts)
|
|
fxp_init(sc);
|
|
error = 0;
|
|
}
|
|
break;
|
|
|
|
case SIOCSIFMEDIA:
|
|
case SIOCGIFMEDIA:
|
|
error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, command);
|
|
break;
|
|
|
|
default:
|
|
error = EINVAL;
|
|
}
|
|
(void) splx(s);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Program the multicast filter.
|
|
*
|
|
* We have an artificial restriction that the multicast setup command
|
|
* must be the first command in the chain, so we take steps to ensure
|
|
* that. By requiring this, it allows us to keep the performance of
|
|
* the pre-initialized command ring (esp. link pointers) by not actually
|
|
* inserting the mcsetup command in the ring - i.e. it's link pointer
|
|
* points to the TxCB ring, but the mcsetup descriptor itself is not part
|
|
* of it. We then can do 'CU_START' on the mcsetup descriptor and have it
|
|
* lead into the regular TxCB ring when it completes.
|
|
*
|
|
* This function must be called at splnet.
|
|
*/
|
|
static void
|
|
fxp_mc_setup(sc)
|
|
struct fxp_softc *sc;
|
|
{
|
|
struct fxp_cb_mcs *mcsp = &sc->control_data->fcd_mcscb;
|
|
struct ifnet *ifp = &sc->sc_if;
|
|
struct ethercom *ec = &sc->sc_ethercom;
|
|
struct ether_multi *enm;
|
|
struct ether_multistep step;
|
|
int nmcasts;
|
|
|
|
if (sc->tx_queued) {
|
|
sc->need_mcsetup = 1;
|
|
return;
|
|
}
|
|
sc->need_mcsetup = 0;
|
|
|
|
/*
|
|
* Initialize multicast setup descriptor.
|
|
*/
|
|
mcsp->cb_soft.next = sc->control_data->fcd_txcbs;
|
|
mcsp->cb_soft.mb_head = NULL;
|
|
mcsp->cb_soft.dmamap = NULL;
|
|
mcsp->cb_status = 0;
|
|
mcsp->cb_command = FXP_CB_COMMAND_MCAS | FXP_CB_COMMAND_S;
|
|
mcsp->link_addr = sc->sc_cddma + FXP_CDOFF(fcd_txcbs[0].cb_status);
|
|
|
|
nmcasts = 0;
|
|
if (!sc->all_mcasts) {
|
|
ETHER_FIRST_MULTI(step, ec, enm);
|
|
while (enm != NULL) {
|
|
/*
|
|
* Check for too many multicast addresses or if we're
|
|
* listening to a range. Either way, we simply have
|
|
* to accept all multicasts.
|
|
*/
|
|
if (nmcasts >= MAXMCADDR ||
|
|
bcmp(enm->enm_addrlo, enm->enm_addrhi,
|
|
ETHER_ADDR_LEN) != 0) {
|
|
sc->all_mcasts = 1;
|
|
nmcasts = 0;
|
|
break;
|
|
}
|
|
bcopy(enm->enm_addrlo,
|
|
(void *)
|
|
&sc->control_data->fcd_mcscb.mc_addr[nmcasts][0],
|
|
ETHER_ADDR_LEN);
|
|
nmcasts++;
|
|
ETHER_NEXT_MULTI(step, enm);
|
|
}
|
|
}
|
|
mcsp->mc_cnt = nmcasts * 6;
|
|
sc->cbl_first = sc->cbl_last = (struct fxp_cb_tx *) mcsp;
|
|
sc->tx_queued = 1;
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
FXP_CDOFF(fcd_mcscb.cb_status), FXP_MCSDESCSIZE,
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
/*
|
|
* Wait until command unit is not active. This should never
|
|
* be the case when nothing is queued, but make sure anyway.
|
|
*/
|
|
while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) ==
|
|
FXP_SCB_CUS_ACTIVE) ;
|
|
|
|
/*
|
|
* Start the multicast setup command.
|
|
*/
|
|
fxp_scb_wait(sc);
|
|
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL,
|
|
sc->sc_cddma + FXP_CDOFF(fcd_mcscb.cb_status));
|
|
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
|
|
|
|
ifp->if_timer = 5;
|
|
return;
|
|
}
|