/* $NetBSD: if_dge.c,v 1.65 2024/07/05 04:31:51 rin Exp $ */
/*
* Copyright (c) 2004, SUNET, Swedish University Computer Network.
* All rights reserved.
*
* Written by Anders Magnusson for SUNET, Swedish University Computer Network.
*
* 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 for the NetBSD Project by
* SUNET, Swedish University Computer Network.
* 4. The name of SUNET may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY SUNET ``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 WASABI SYSTEMS, INC
* 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) 2001, 2002, 2003 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Jason R. Thorpe for Wasabi Systems, Inc.
*
* 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 for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
* 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.
*/
/*
* Device driver for the Intel 82597EX Ten Gigabit Ethernet controller.
*
* TODO (in no specific order):
* HW VLAN support.
* TSE offloading (needs kernel changes...)
* RAIDC (receive interrupt delay adaptation)
* Use memory > 4GB.
*/
#include <netinet/in.h> /* XXX for struct ip */
#include <netinet/in_systm.h> /* XXX for struct ip */
#include <netinet/ip.h> /* XXX for struct ip */
#include <netinet/tcp.h> /* XXX for struct tcphdr */
/*
* The receive engine may sometimes become off-by-one when writing back
* chained descriptors. Avoid this by allocating a large chunk of
* memory and use if instead (to avoid chained descriptors).
* This only happens with chained descriptors under heavy load.
*/
#define DGE_OFFBYONE_RXBUG
#define DPRINTF(x, y) if (dge_debug & (x)) printf y
#else
#define DPRINTF(x, y) /* nothing */
#endif /* DGE_DEBUG */
/*
* Transmit descriptor list size. We allow up to 100 DMA segments per
* packet (Intel reports of jumbo frame packets with as
* many as 80 DMA segments when using 16k buffers).
*/
#define DGE_NTXSEGS 100
#define DGE_IFQUEUELEN 20000
#define DGE_TXQUEUELEN 2048
#define DGE_TXQUEUELEN_MASK (DGE_TXQUEUELEN - 1)
#define DGE_TXQUEUE_GC (DGE_TXQUEUELEN / 8)
#define DGE_NTXDESC 1024
#define DGE_NTXDESC_MASK (DGE_NTXDESC - 1)
#define DGE_NEXTTX(x) (((x) + 1) & DGE_NTXDESC_MASK)
#define DGE_NEXTTXS(x) (((x) + 1) & DGE_TXQUEUELEN_MASK)
/*
* Receive descriptor list size.
* Packet is of size MCLBYTES, and for jumbo packets buffers may
* be chained. Due to the nature of the card (high-speed), keep this
* ring large. With 2k buffers the ring can store 400 jumbo packets,
* which at full speed will be received in just under 3ms.
*/
#define DGE_NRXDESC 2048
#define DGE_NRXDESC_MASK (DGE_NRXDESC - 1)
#define DGE_NEXTRX(x) (((x) + 1) & DGE_NRXDESC_MASK)
/*
* # of descriptors between head and written descriptors.
* This is to work-around two erratas.
*/
#define DGE_RXSPACE 10
#define DGE_PREVRX(x) (((x) - DGE_RXSPACE) & DGE_NRXDESC_MASK)
/*
* Receive descriptor fetch thresholds. These are values recommended
* by Intel, do not touch them unless you know what you are doing.
*/
#define RXDCTL_PTHRESH_VAL 128
#define RXDCTL_HTHRESH_VAL 16
#define RXDCTL_WTHRESH_VAL 16
/*
* Tweakable parameters; default values.
*/
#define FCRTH 0x30000 /* Send XOFF water mark */
#define FCRTL 0x28000 /* Send XON water mark */
#define RDTR 0x20 /* Interrupt delay after receive, .8192us units */
#define TIDV 0x20 /* Interrupt delay after send, .8192us units */
/*
* Control structures are DMA'd to the i82597 chip. We allocate them in
* a single clump that maps to a single DMA segment to make serveral things
* easier.
*/
struct dge_control_data {
/*
* The transmit descriptors.
*/
struct dge_tdes wcd_txdescs[DGE_NTXDESC];
/*
* The receive descriptors.
*/
struct dge_rdes wcd_rxdescs[DGE_NRXDESC];
};
/*
* The DGE interface have a higher max MTU size than normal jumbo frames.
*/
#define DGE_MAX_MTU 16288 /* Max MTU size for this interface */
/*
* Software state for transmit jobs.
*/
struct dge_txsoft {
struct mbuf *txs_mbuf; /* head of our mbuf chain */
bus_dmamap_t txs_dmamap; /* our DMA map */
int txs_firstdesc; /* first descriptor in packet */
int txs_lastdesc; /* last descriptor in packet */
int txs_ndesc; /* # of descriptors used */
};
/*
* Software state for receive buffers. Each descriptor gets a
* 2k (MCLBYTES) buffer and a DMA map. For packets which fill
* more than one buffer, we chain them together.
*/
struct dge_rxsoft {
struct mbuf *rxs_mbuf; /* head of our mbuf chain */
bus_dmamap_t rxs_dmamap; /* our DMA map */
};
/*
* Software state per device.
*/
struct dge_softc {
device_t sc_dev; /* generic device information */
bus_space_tag_t sc_st; /* bus space tag */
bus_space_handle_t sc_sh; /* bus space handle */
bus_dma_tag_t sc_dmat; /* bus DMA tag */
struct ethercom sc_ethercom; /* ethernet common data */
int sc_flags; /* flags; see below */
int sc_bus_speed; /* PCI/PCIX bus speed */
int sc_pcix_offset; /* PCIX capability register offset */
const struct dge_product *sc_dgep; /* Pointer to the dge_product entry */
pci_chipset_tag_t sc_pc;
pcitag_t sc_pt;
int sc_mmrbc; /* Max PCIX memory read byte count */
void *sc_ih; /* interrupt cookie */
struct ifmedia sc_media;
bus_dmamap_t sc_cddmamap; /* control data DMA map */
#define sc_cddma sc_cddmamap->dm_segs[0].ds_addr
int sc_align_tweak;
/*
* Software state for the transmit and receive descriptors.
*/
struct dge_txsoft sc_txsoft[DGE_TXQUEUELEN];
struct dge_rxsoft sc_rxsoft[DGE_NRXDESC];
/*
* Control data structures.
*/
struct dge_control_data *sc_control_data;
#define sc_txdescs sc_control_data->wcd_txdescs
#define sc_rxdescs sc_control_data->wcd_rxdescs
#ifdef DGE_EVENT_COUNTERS
/* Event counters. */
struct evcnt sc_ev_txsstall; /* Tx stalled due to no txs */
struct evcnt sc_ev_txdstall; /* Tx stalled due to no txd */
struct evcnt sc_ev_txforceintr; /* Tx interrupts forced */
struct evcnt sc_ev_txdw; /* Tx descriptor interrupts */
struct evcnt sc_ev_txqe; /* Tx queue empty interrupts */
struct evcnt sc_ev_rxintr; /* Rx interrupts */
struct evcnt sc_ev_linkintr; /* Link interrupts */
int sc_rxptr; /* next ready Rx descriptor/queue ent */
int sc_rxdiscard;
int sc_rxlen;
struct mbuf *sc_rxhead;
struct mbuf *sc_rxtail;
struct mbuf **sc_rxtailp;
state = 4;
sc->sc_bugbuf = (void *)kva;
SLIST_INIT(&sc->sc_buglist);
/*
* Now divide it up into DGE_BUFFER_SIZE pieces and save the addresses
* in an array.
*/
entry = malloc(sizeof(*entry) * DGE_NBUFFERS, M_DEVBUF, M_WAITOK);
sc->sc_entry = entry;
for (i = 0; i < DGE_NBUFFERS; i++) {
entry[i].rb_slot = i;
SLIST_INSERT_HEAD(&sc->sc_buglist, &entry[i], rb_entry);
}
out:
if (error != 0) {
switch (state) {
case 4:
bus_dmamap_unload(sc->sc_dmat, sc->sc_bugmap);
/* FALLTHROUGH */
case 3:
bus_dmamap_destroy(sc->sc_dmat, sc->sc_bugmap);
/* FALLTHROUGH */
case 2:
bus_dmamem_unmap(sc->sc_dmat, kva, DGE_RXMEM);
/* FALLTHROUGH */
case 1:
bus_dmamem_free(sc->sc_dmat, &seg, rseg);
break;
default:
break;
}
}
/*
* Allocate the control data structures, and create and load the
* DMA map for it.
*/
if ((error = bus_dmamem_alloc(sc->sc_dmat,
sizeof(struct dge_control_data), PAGE_SIZE, 0, &seg, 1, &rseg,
0)) != 0) {
aprint_error_dev(sc->sc_dev,
"unable to allocate control data, error = %d\n",
error);
goto fail_0;
}
if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg,
sizeof(struct dge_control_data), (void **)&sc->sc_control_data,
0)) != 0) {
aprint_error_dev(sc->sc_dev,
"unable to map control data, error = %d\n", error);
goto fail_1;
}
if ((error = bus_dmamap_create(sc->sc_dmat,
sizeof(struct dge_control_data), 1,
sizeof(struct dge_control_data), 0, 0, &sc->sc_cddmamap)) != 0) {
aprint_error_dev(sc->sc_dev, "unable to create control data "
"DMA map, error = %d\n", error);
goto fail_2;
}
if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_cddmamap,
sc->sc_control_data, sizeof(struct dge_control_data), NULL,
0)) != 0) {
aprint_error_dev(sc->sc_dev,
"unable to load control data DMA map, error = %d\n",
error);
goto fail_3;
}
#ifdef DGE_OFFBYONE_RXBUG
if (dge_alloc_rcvmem(sc) != 0)
return; /* Already complained */
#endif
/*
* Create the transmit buffer DMA maps.
*/
for (i = 0; i < DGE_TXQUEUELEN; i++) {
if ((error = bus_dmamap_create(sc->sc_dmat, DGE_MAX_MTU,
DGE_NTXSEGS, MCLBYTES, 0, 0,
&sc->sc_txsoft[i].txs_dmamap)) != 0) {
aprint_error_dev(sc->sc_dev, "unable to create Tx DMA map %d, "
"error = %d\n", i, error);
goto fail_4;
}
}
/*
* Create the receive buffer DMA maps.
*/
for (i = 0; i < DGE_NRXDESC; i++) {
#ifdef DGE_OFFBYONE_RXBUG
if ((error = bus_dmamap_create(sc->sc_dmat, DGE_BUFFER_SIZE, 1,
DGE_BUFFER_SIZE, 0, 0, &sc->sc_rxsoft[i].rxs_dmamap)) != 0) {
#else
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
MCLBYTES, 0, 0, &sc->sc_rxsoft[i].rxs_dmamap)) != 0) {
#endif
aprint_error_dev(sc->sc_dev, "unable to create Rx DMA "
"map %d, error = %d\n", i, error);
goto fail_5;
}
sc->sc_rxsoft[i].rxs_mbuf = NULL;
}
/*
* Set bits in ctrl0 register.
* Should get the software defined pins out of EEPROM?
*/
sc->sc_ctrl0 |= CTRL0_RPE | CTRL0_TPE; /* XON/XOFF */
sc->sc_ctrl0 |= CTRL0_SDP3_DIR | CTRL0_SDP2_DIR | CTRL0_SDP1_DIR |
CTRL0_SDP0_DIR | CTRL0_SDP3 | CTRL0_SDP2 | CTRL0_SDP0;
/*
* Reset the chip to a known state.
*/
dge_reset(sc);
/*
* Reset the PHY.
*/
dge_xgmii_reset(sc);
/*
* Read in EEPROM data.
*/
if (dge_read_eeprom(sc)) {
aprint_error_dev(sc->sc_dev, "couldn't read EEPROM\n");
return;
}
for (i = 0; i < DGE_NTXSEGS; i++)
evcnt_attach_dynamic(&sc->sc_ev_txseg[i], EVCNT_TYPE_MISC,
NULL, device_xname(sc->sc_dev), (*dge_txseg_evcnt_names)[i]);
/*
* Make sure the interface is shutdown during reboot.
*/
if (pmf_device_register1(self, NULL, NULL, dge_shutdown))
pmf_class_network_register(self, ifp);
else
aprint_error_dev(self, "couldn't establish power handler\n");
return;
/*
* Free any resources we've allocated during the failed attach
* attempt. Do this in reverse order and fall through.
*/
fail_5:
for (i = 0; i < DGE_NRXDESC; i++) {
if (sc->sc_rxsoft[i].rxs_dmamap != NULL)
bus_dmamap_destroy(sc->sc_dmat,
sc->sc_rxsoft[i].rxs_dmamap);
}
fail_4:
for (i = 0; i < DGE_TXQUEUELEN; i++) {
if (sc->sc_txsoft[i].txs_dmamap != NULL)
bus_dmamap_destroy(sc->sc_dmat,
sc->sc_txsoft[i].txs_dmamap);
}
bus_dmamap_unload(sc->sc_dmat, sc->sc_cddmamap);
fail_3:
bus_dmamap_destroy(sc->sc_dmat, sc->sc_cddmamap);
fail_2:
bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_control_data,
sizeof(struct dge_control_data));
fail_1:
bus_dmamem_free(sc->sc_dmat, &seg, rseg);
fail_0:
return;
}
/*
* dge_shutdown:
*
* Make sure the interface is stopped at reboot time.
*/
static bool
dge_shutdown(device_t self, int howto)
{
struct dge_softc *sc;
/*
* NOTE: Even if we're not using the IP or TCP/UDP checksum
* offload feature, if we load the context descriptor, we
* MUST provide valid values for IPCSS and TUCSS fields.
*/
if (m0->m_pkthdr.csum_flags & M_CSUM_IPv4) {
DGE_EVCNT_INCR(&sc->sc_ev_txipsum);
fields |= TDESC_POPTS_IXSM;
ipcs = DGE_TCPIP_IPCSS(offset) |
DGE_TCPIP_IPCSO(offset + offsetof(struct ip, ip_sum)) |
DGE_TCPIP_IPCSE(offset + iphl - 1);
} else if (__predict_true(sc->sc_txctx_ipcs != 0xffffffff)) {
/* Use the cached value. */
ipcs = sc->sc_txctx_ipcs;
} else {
/* Just initialize it to the likely value anyway. */
ipcs = DGE_TCPIP_IPCSS(offset) |
DGE_TCPIP_IPCSO(offset + offsetof(struct ip, ip_sum)) |
DGE_TCPIP_IPCSE(offset + iphl - 1);
}
DPRINTF(DGE_DEBUG_CKSUM,
("%s: CKSUM: offset %d ipcs 0x%x\n",
device_xname(sc->sc_dev), offset, ipcs));
offset += iphl;
if (m0->m_pkthdr.csum_flags & (M_CSUM_TCPv4 | M_CSUM_UDPv4)) {
DGE_EVCNT_INCR(&sc->sc_ev_txtusum);
fields |= TDESC_POPTS_TXSM;
tucs = DGE_TCPIP_TUCSS(offset) |
DGE_TCPIP_TUCSO(offset + M_CSUM_DATA_IPv4_OFFSET(m0->m_pkthdr.csum_data)) |
DGE_TCPIP_TUCSE(0) /* rest of packet */;
} else if (__predict_true(sc->sc_txctx_tucs != 0xffffffff)) {
/* Use the cached value. */
tucs = sc->sc_txctx_tucs;
} else {
/* Just initialize it to a valid TCP context. */
tucs = DGE_TCPIP_TUCSS(offset) |
DGE_TCPIP_TUCSO(offset + offsetof(struct tcphdr, th_sum)) |
DGE_TCPIP_TUCSE(0) /* rest of packet */;
}
if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
return;
/*
* Remember the previous number of free descriptors.
*/
ofree = sc->sc_txfree;
/*
* Loop through the send queue, setting up transmit descriptors
* until we drain the queue, or use up all available transmit
* descriptors.
*/
for (;;) {
/* Grab a packet off the queue. */
IFQ_POLL(&ifp->if_snd, m0);
if (m0 == NULL)
break;
DPRINTF(DGE_DEBUG_TX,
("%s: TX: have packet to transmit: %p\n",
device_xname(sc->sc_dev), m0));
/* Get a work queue entry. */
if (sc->sc_txsfree < DGE_TXQUEUE_GC) {
dge_txintr(sc);
if (sc->sc_txsfree == 0) {
DPRINTF(DGE_DEBUG_TX,
("%s: TX: no free job descriptors\n",
device_xname(sc->sc_dev)));
DGE_EVCNT_INCR(&sc->sc_ev_txsstall);
break;
}
}
/*
* Load the DMA map. If this fails, the packet either
* didn't fit in the allotted number of segments, or we
* were short on resources. For the too-many-segments
* case, we simply report an error and drop the packet,
* since we can't sanely copy a jumbo packet to a single
* buffer.
*/
error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0,
BUS_DMA_WRITE | BUS_DMA_NOWAIT);
if (error) {
if (error == EFBIG) {
DGE_EVCNT_INCR(&sc->sc_ev_txdrop);
printf("%s: Tx packet consumes too many "
"DMA segments, dropping...\n",
device_xname(sc->sc_dev));
IFQ_DEQUEUE(&ifp->if_snd, m0);
m_freem(m0);
continue;
}
/*
* Short on resources, just stop for now.
*/
DPRINTF(DGE_DEBUG_TX,
("%s: TX: dmamap load failed: %d\n",
device_xname(sc->sc_dev), error));
break;
}
/*
* Ensure we have enough descriptors free to describe
* the packet. Note, we always reserve one descriptor
* at the end of the ring due to the semantics of the
* TDT register, plus one more in the event we need
* to re-load checksum offload context.
*/
if (dmamap->dm_nsegs > (sc->sc_txfree - 2)) {
/*
* Not enough free descriptors to transmit this
* packet. We haven't committed anything yet,
* so just unload the DMA map, put the packet
* pack on the queue, and punt. Notify the upper
* layer that there are no more slots left.
*/
DPRINTF(DGE_DEBUG_TX,
("%s: TX: need %d descriptors, have %d\n",
device_xname(sc->sc_dev), dmamap->dm_nsegs,
sc->sc_txfree - 1));
ifp->if_flags |= IFF_OACTIVE;
bus_dmamap_unload(sc->sc_dmat, dmamap);
DGE_EVCNT_INCR(&sc->sc_ev_txdstall);
break;
}
IFQ_DEQUEUE(&ifp->if_snd, m0);
/*
* WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET.
*/
/*
* Store a pointer to the packet so that we can free it
* later.
*
* Initially, we consider the number of descriptors the
* packet uses the number of DMA segments. This may be
* incremented by 1 if we do checksum offload (a descriptor
* is used to set the checksum context).
*/
txs->txs_mbuf = m0;
txs->txs_firstdesc = sc->sc_txnext;
txs->txs_ndesc = dmamap->dm_nsegs;
/*
* Set up checksum offload parameters for
* this packet.
*/
if (m0->m_pkthdr.csum_flags &
(M_CSUM_IPv4 | M_CSUM_TCPv4 | M_CSUM_UDPv4)) {
if (dge_tx_cksum(sc, txs, &cksumfields) != 0) {
/* Error message already displayed. */
bus_dmamap_unload(sc->sc_dmat, dmamap);
continue;
}
} else {
cksumfields = 0;
}
/*
* Set up the command byte on the last descriptor of
* the packet. If we're in the interrupt delay window,
* delay the interrupt.
*/
sc->sc_txdescs[lasttx].dt_ctl |=
htole32(TDESC_DCMD_EOP | TDESC_DCMD_RS);
/*
* Go through the Tx list and free mbufs for those
* frames which have been transmitted.
*/
for (i = sc->sc_txsdirty; sc->sc_txsfree != DGE_TXQUEUELEN;
i = DGE_NEXTTXS(i), sc->sc_txsfree++) {
txs = &sc->sc_txsoft[i];
/*
* Add a new receive buffer to the ring.
*/
if (dge_add_rxbuf(sc, i) != 0) {
/*
* Failed, throw away what we've done so
* far, and discard the rest of the packet.
*/
if_statinc(ifp, if_ierrors);
bus_dmamap_sync(sc->sc_dmat, rxs->rxs_dmamap, 0,
rxs->rxs_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD);
DGE_INIT_RXDESC(sc, i);
if ((status & RDESC_STS_EOP) == 0)
sc->sc_rxdiscard = 1;
m_freem(sc->sc_rxhead);
DGE_RXCHAIN_RESET(sc);
DPRINTF(DGE_DEBUG_RX,
("%s: RX: Rx buffer allocation failed, "
"dropping packet%s\n", device_xname(sc->sc_dev),
sc->sc_rxdiscard ? " (discard)" : ""));
continue;
}
DGE_INIT_RXDESC(sc, DGE_PREVRX(i)); /* Write the descriptor */
DGE_RXCHAIN_LINK(sc, m);
m->m_len = len;
DPRINTF(DGE_DEBUG_RX,
("%s: RX: buffer at %p len %d\n",
device_xname(sc->sc_dev), m->m_data, len));
/*
* If this is not the end of the packet, keep
* looking.
*/
if ((status & RDESC_STS_EOP) == 0) {
sc->sc_rxlen += len;
DPRINTF(DGE_DEBUG_RX,
("%s: RX: not yet EOP, rxlen -> %d\n",
device_xname(sc->sc_dev), sc->sc_rxlen));
continue;
}
/*
* Okay, we have the entire packet now...
*/
*sc->sc_rxtailp = NULL;
m = sc->sc_rxhead;
len += sc->sc_rxlen;
DGE_RXCHAIN_RESET(sc);
DPRINTF(DGE_DEBUG_RX,
("%s: RX: have entire packet, len -> %d\n",
device_xname(sc->sc_dev), len));
/*
* If an error occurred, update stats and drop the packet.
*/
if (errors & (RDESC_ERR_CE | RDESC_ERR_SE | RDESC_ERR_P |
RDESC_ERR_RXE)) {
if_statinc(ifp, if_ierrors);
if (errors & RDESC_ERR_SE)
printf("%s: symbol error\n",
device_xname(sc->sc_dev));
else if (errors & RDESC_ERR_P)
printf("%s: parity error\n",
device_xname(sc->sc_dev));
else if (errors & RDESC_ERR_CE)
printf("%s: CRC error\n",
device_xname(sc->sc_dev));
m_freem(m);
continue;
}
/*
* No errors. Receive the packet.
*/
m_set_rcvif(m, ifp);
m->m_pkthdr.len = len;
/*
* Set up checksum info for this packet.
*/
if (status & RDESC_STS_IPCS) {
DGE_EVCNT_INCR(&sc->sc_ev_rxipsum);
m->m_pkthdr.csum_flags |= M_CSUM_IPv4;
if (errors & RDESC_ERR_IPE)
m->m_pkthdr.csum_flags |= M_CSUM_IPv4_BAD;
}
if (status & RDESC_STS_TCPCS) {
/*
* Note: we don't know if this was TCP or UDP,
* so we just set both bits, and expect the
* upper layers to deal.
*/
DGE_EVCNT_INCR(&sc->sc_ev_rxtusum);
m->m_pkthdr.csum_flags |= M_CSUM_TCPv4 | M_CSUM_UDPv4;
if (errors & RDESC_ERR_TCPE)
m->m_pkthdr.csum_flags |= M_CSUM_TCP_UDP_BAD;
}
/* Pass it on. */
if_percpuq_enqueue(ifp->if_percpuq, m);
}
/* Update the receive pointer. */
sc->sc_rxptr = i;
/*
* dge_reset:
*
* Reset the i82597 chip.
*/
static void
dge_reset(struct dge_softc *sc)
{
int i;
/*
* Do a chip reset.
*/
CSR_WRITE(sc, DGE_CTRL0, CTRL0_RST | sc->sc_ctrl0);
delay(10000);
for (i = 0; i < 1000; i++) {
if ((CSR_READ(sc, DGE_CTRL0) & CTRL0_RST) == 0)
break;
delay(20);
}
if (CSR_READ(sc, DGE_CTRL0) & CTRL0_RST)
printf("%s: WARNING: reset failed to complete\n",
device_xname(sc->sc_dev));
/*
* Reset the EEPROM logic.
* This will cause the chip to reread its default values,
* which doesn't happen otherwise (errata).
*/
CSR_WRITE(sc, DGE_CTRL1, CTRL1_EE_RST);
delay(10000);
}
/*
* dge_init: [ifnet interface function]
*
* Initialize the interface. Must be called at splnet().
*/
static int
dge_init(struct ifnet *ifp)
{
struct dge_softc *sc = ifp->if_softc;
struct dge_rxsoft *rxs;
int i, error = 0;
uint32_t reg;
/*
* *_HDR_ALIGNED_P is constant 1 if __NO_STRICT_ALIGNMENT is set.
* There is a small but measurable benefit to avoiding the adjusment
* of the descriptor so that the headers are aligned, for normal mtu,
* on such platforms. One possibility is that the DMA itself is
* slightly more efficient if the front of the entire packet (instead
* of the front of the headers) is aligned.
*
* Note we must always set align_tweak to 0 if we are using
* jumbo frames.
*/
#ifdef __NO_STRICT_ALIGNMENT
sc->sc_align_tweak = 0;
#else
if ((ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN) > (MCLBYTES - 2))
sc->sc_align_tweak = 0;
else
sc->sc_align_tweak = 2;
#endif /* __NO_STRICT_ALIGNMENT */
/* Cancel any pending I/O. */
dge_stop(ifp, 0);
/* Reset the chip to a known state. */
dge_reset(sc);
/*
* Set up the interrupt registers.
*/
CSR_WRITE(sc, DGE_IMC, 0xffffffffU);
sc->sc_icr = ICR_TXDW | ICR_LSC | ICR_RXSEQ | ICR_RXDMT0 |
ICR_RXO | ICR_RXT0;
CSR_WRITE(sc, DGE_IMS, sc->sc_icr);
/*
* Set up the transmit control register.
*/
sc->sc_tctl = TCTL_TCE | TCTL_TPDE | TCTL_TXEN;
CSR_WRITE(sc, DGE_TCTL, sc->sc_tctl);
/*
* Set up the receive control register; we actually program
* the register when we set the receive filter. Use multicast
* address offset type 0.
*/
sc->sc_mchash_type = 0;
/*
* dge_set_filter:
*
* Set up the receive filter.
*/
static void
dge_set_filter(struct dge_softc *sc)
{
struct ethercom *ec = &sc->sc_ethercom;
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
struct ether_multi *enm;
struct ether_multistep step;
uint32_t hash, reg, bit;
int i;
sc->sc_rctl &= ~(RCTL_BAM | RCTL_UPE | RCTL_MPE);
if (ifp->if_flags & IFF_BROADCAST)
sc->sc_rctl |= RCTL_BAM;
if (ifp->if_flags & IFF_PROMISC) {
sc->sc_rctl |= RCTL_UPE;
goto allmulti;
}
/*
* Set the station address in the first RAL slot, and
* clear the remaining slots.
*/
dge_set_ral(sc, CLLADDR(ifp->if_sadl), 0);
for (i = 1; i < RA_TABSIZE; i++)
dge_set_ral(sc, NULL, i);
/* Clear out the multicast table. */
for (i = 0; i < MC_TABSIZE; i++)
CSR_WRITE(sc, DGE_MTA + (i << 2), 0);
ETHER_LOCK(ec);
ETHER_FIRST_MULTI(step, ec, enm);
while (enm != NULL) {
if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
/*
* We must listen to a range of multicast addresses.
* For now, just accept all multicasts, rather than
* trying to set only those filter bits needed to match
* the range. (At this time, the only use of address
* ranges is for IP multicast routing, for which the
* range is big enough to require all bits set.)
*/
ETHER_UNLOCK(ec);
goto allmulti;
}
/*
* Read in the EEPROM info and verify checksum.
*/
int
dge_read_eeprom(struct dge_softc *sc)
{
uint16_t cksum;
int i;
cksum = 0;
for (i = 0; i < EEPROM_SIZE; i++) {
sc->sc_eeprom[i] = dge_eeprom_word(sc, i);
cksum += sc->sc_eeprom[i];
}
return cksum != EEPROM_CKSUM;
}
/*
* Read a 16-bit word from address addr in the serial EEPROM.
*/
uint16_t
dge_eeprom_word(struct dge_softc *sc, int addr)
{
uint32_t reg;
uint16_t rval = 0;
int i;