summaryrefslogtreecommitdiff
path: root/sys/dev/ic/gem.c
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2013-08-08 16:01:35 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2013-08-08 16:01:35 +0000
commit0383a360d1897e0c4940ac8a5264aa27a4c3a45b (patch)
tree1f91ae297322371ddec04c99626b7e6b21b61011 /sys/dev/ic/gem.c
parent98f78c038f6b3ce25fa8263a44ee251734c85bcb (diff)
Turns out neither the flow control diff nor the RX checksum offload diff
is responsible for the hangs seen by Henning. So put them back.
Diffstat (limited to 'sys/dev/ic/gem.c')
-rw-r--r--sys/dev/ic/gem.c129
1 files changed, 121 insertions, 8 deletions
diff --git a/sys/dev/ic/gem.c b/sys/dev/ic/gem.c
index 2318840356c..514c6fb2476 100644
--- a/sys/dev/ic/gem.c
+++ b/sys/dev/ic/gem.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: gem.c,v 1.100 2013/07/25 19:50:56 kettenis Exp $ */
+/* $OpenBSD: gem.c,v 1.101 2013/08/08 16:01:34 kettenis Exp $ */
/* $NetBSD: gem.c,v 1.1 2001/09/16 00:11:43 eeh Exp $ */
/*
@@ -56,7 +56,11 @@
#ifdef INET
#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
#endif
#if NBPFILTER > 0
@@ -116,6 +120,7 @@ int gem_eint(struct gem_softc *, u_int);
int gem_rint(struct gem_softc *);
int gem_tint(struct gem_softc *, u_int32_t);
int gem_pint(struct gem_softc *);
+void gem_rxcksum(struct mbuf *, u_int64_t);
#ifdef GEM_DEBUG
#define DPRINTF(sc, x) if ((sc)->sc_arpcom.ac_if.if_flags & IFF_DEBUG) \
@@ -133,7 +138,7 @@ gem_config(struct gem_softc *sc)
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
struct mii_data *mii = &sc->sc_mii;
struct mii_softc *child;
- int i, error, phyad;
+ int i, error, mii_flags, phyad;
struct ifmedia_entry *ifm;
/* Make sure the chip is stopped. */
@@ -247,6 +252,8 @@ gem_config(struct gem_softc *sc)
gem_mifinit(sc);
+ mii_flags = MIIF_DOPAUSE;
+
/*
* Look for an external PHY.
*/
@@ -265,7 +272,7 @@ gem_config(struct gem_softc *sc)
}
mii_attach(&sc->sc_dev, mii, 0xffffffff, phyad,
- MII_OFFSET_ANY, 0);
+ MII_OFFSET_ANY, mii_flags);
}
/*
@@ -294,7 +301,7 @@ gem_config(struct gem_softc *sc)
}
mii_attach(&sc->sc_dev, mii, 0xffffffff, phyad,
- MII_OFFSET_ANY, 0);
+ MII_OFFSET_ANY, mii_flags);
}
/*
@@ -316,8 +323,10 @@ gem_config(struct gem_softc *sc)
mii->mii_readreg = gem_pcs_readreg;
mii->mii_writereg = gem_pcs_writereg;
+ mii_flags |= MIIF_NOISOLATE;
+
mii_attach(&sc->sc_dev, mii, 0xffffffff, MII_PHY_ANY,
- MII_OFFSET_ANY, MIIF_NOISOLATE);
+ MII_OFFSET_ANY, mii_flags);
}
child = LIST_FIRST(&mii->mii_phys);
@@ -802,7 +811,9 @@ gem_init(struct ifnet *ifp)
/* Encode Receive Descriptor ring size: four possible values */
v = gem_ringsize(GEM_NRXDESC /*XXX*/);
-
+ /* RX TCP/UDP checksum offset */
+ v |= ((ETHER_HDR_LEN + sizeof(struct ip)) <<
+ GEM_RX_CONFIG_CXM_START_SHFT);
/* Enable DMA */
bus_space_write_4(t, h, GEM_RX_CONFIG,
v|(GEM_THRSH_1024<<GEM_RX_CONFIG_FIFO_THRS_SHIFT)|
@@ -906,8 +917,8 @@ gem_init_regs(struct gem_softc *sc)
bus_space_write_4(t, h, GEM_MAC_RX_CRC_ERR_CNT, 0);
bus_space_write_4(t, h, GEM_MAC_RX_CODE_VIOL, 0);
- /* Un-pause stuff */
- bus_space_write_4(t, h, GEM_MAC_SEND_PAUSE_CMD, 0);
+ /* Set XOFF PAUSE time */
+ bus_space_write_4(t, h, GEM_MAC_SEND_PAUSE_CMD, 0x1bf0);
/*
* Set the internal arbitration to "infinite" bursts of the
@@ -937,6 +948,95 @@ gem_init_regs(struct gem_softc *sc)
}
/*
+ * RX TCP/UDP checksum
+ */
+void
+gem_rxcksum(struct mbuf *m, u_int64_t rxstat)
+{
+ struct ether_header *eh;
+ struct ip *ip;
+ struct udphdr *uh;
+ int32_t hlen, len, pktlen;
+ u_int16_t cksum, *opts;
+ u_int32_t temp32;
+ union pseudoh {
+ struct hdr {
+ u_int16_t len;
+ u_int8_t ttl;
+ u_int8_t proto;
+ u_int32_t src;
+ u_int32_t dst;
+ } h;
+ u_int16_t w[6];
+ } ph;
+
+ pktlen = m->m_pkthdr.len;
+ if (pktlen < sizeof(struct ether_header))
+ return;
+ eh = mtod(m, struct ether_header *);
+ if (eh->ether_type != htons(ETHERTYPE_IP))
+ return;
+ ip = (struct ip *)(eh + 1);
+ if (ip->ip_v != IPVERSION)
+ return;
+
+ hlen = ip->ip_hl << 2;
+ pktlen -= sizeof(struct ether_header);
+ if (hlen < sizeof(struct ip))
+ return;
+ if (ntohs(ip->ip_len) < hlen)
+ return;
+ if (ntohs(ip->ip_len) != pktlen)
+ return;
+ if (ip->ip_off & htons(IP_MF | IP_OFFMASK))
+ return; /* can't handle fragmented packet */
+
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ if (pktlen < (hlen + sizeof(struct tcphdr)))
+ return;
+ break;
+ case IPPROTO_UDP:
+ if (pktlen < (hlen + sizeof(struct udphdr)))
+ return;
+ uh = (struct udphdr *)((caddr_t)ip + hlen);
+ if (uh->uh_sum == 0)
+ return; /* no checksum */
+ break;
+ default:
+ return;
+ }
+
+ cksum = htons(~(rxstat & GEM_RD_CHECKSUM));
+ /* cksum fixup for IP options */
+ len = hlen - sizeof(struct ip);
+ if (len > 0) {
+ opts = (u_int16_t *)(ip + 1);
+ for (; len > 0; len -= sizeof(u_int16_t), opts++) {
+ temp32 = cksum - *opts;
+ temp32 = (temp32 >> 16) + (temp32 & 65535);
+ cksum = temp32 & 65535;
+ }
+ }
+
+ ph.h.len = htons(ntohs(ip->ip_len) - hlen);
+ ph.h.ttl = 0;
+ ph.h.proto = ip->ip_p;
+ ph.h.src = ip->ip_src.s_addr;
+ ph.h.dst = ip->ip_dst.s_addr;
+ temp32 = cksum;
+ opts = &ph.w[0];
+ temp32 += opts[0] + opts[1] + opts[2] + opts[3] + opts[4] + opts[5];
+ temp32 = (temp32 >> 16) + (temp32 & 65535);
+ temp32 += (temp32 >> 16);
+ cksum = ~temp32;
+ if (cksum == 0) {
+ m->m_pkthdr.csum_flags |=
+ M_TCP_CSUM_IN_OK | M_UDP_CSUM_IN_OK;
+ }
+}
+
+/*
* Receive interrupt.
*/
int
@@ -1001,6 +1101,8 @@ gem_rint(struct gem_softc *sc)
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = len;
+ gem_rxcksum(m, rxstat);
+
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
@@ -1361,6 +1463,17 @@ gem_mii_statchg(struct device *dev)
v &= ~GEM_MAC_XIF_GMII_MODE;
}
bus_space_write_4(t, mac, GEM_MAC_XIF_CONFIG, v);
+
+ /*
+ * 802.3x flow control
+ */
+ v = bus_space_read_4(t, mac, GEM_MAC_CONTROL_CONFIG);
+ v &= ~(GEM_MAC_CC_RX_PAUSE | GEM_MAC_CC_TX_PAUSE);
+ if ((IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_ETH_RXPAUSE) != 0)
+ v |= GEM_MAC_CC_RX_PAUSE;
+ if ((IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_ETH_TXPAUSE) != 0)
+ v |= GEM_MAC_CC_TX_PAUSE;
+ bus_space_write_4(t, mac, GEM_MAC_CONTROL_CONFIG, v);
}
int