PERFORCE change 162668 for review
Marius Strobl
marius at FreeBSD.org
Sun May 24 19:50:18 UTC 2009
http://perforce.freebsd.org/chv.cgi?CH=162668
Change 162668 by marius at flak on 2009/05/24 19:49:48
- Improve the alignment checking of the completion and descriptor
rings, remove padding as they are sized in a way so that this
isn't required.
- Implement TCP/UDP checksum offloading.
- Implement support for jumbo frames.
- Some performance improvements and minor bug fixes.
- Turn on RX descriptor cleanup callout as this is still an issue
with these chips and remove CAS_RINT_TIMEOUT as option.
- Add workarounds for some silicon bugs.
- Fix module loading.
- Fix some CAS_DEBUG compilation.
- Don't assign stack garbage as Ethernet address to single-port
cards in !OFW machines.
This driver should be feature complete and close to being
committable to HEAD now.
Affected files ...
.. //depot/projects/usiii/dev/cas/if_cas.c#3 edit
.. //depot/projects/usiii/dev/cas/if_casreg.h#4 edit
.. //depot/projects/usiii/dev/cas/if_casvar.h#3 edit
Differences ...
==== //depot/projects/usiii/dev/cas/if_cas.c#3 (text+ko) ====
@@ -41,10 +41,6 @@
#define CAS_DEBUG
#endif
-#if 0 /* XXX: In case of emergency, re-enable this. */
-#define CAS_RINT_TIMEOUT
-#endif
-
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@@ -96,22 +92,34 @@
#include "miibus_if.h"
-CTASSERT(powerof2(CAS_NRXCOMP) && CAS_NRXCOMP >= 128 && CAS_NRXCOMP <= 32768);
-CTASSERT(powerof2(CAS_NRXDESC) && CAS_NRXDESC >= 32 && CAS_NRXDESC <= 8192);
-CTASSERT(powerof2(CAS_NRXDESC2) && CAS_NRXDESC2 >= 32 && CAS_NRXDESC2 <= 8192);
-CTASSERT(powerof2(CAS_NTXDESC) && CAS_NTXDESC >= 32 && CAS_NTXDESC <= 8192);
+#define RINGASSERT(n , min, max) \
+ CTASSERT(powerof2(n) && (n) >= (min) && (n) <= (max))
+
+RINGASSERT(CAS_NRXCOMP, 128, 32768);
+RINGASSERT(CAS_NRXDESC, 32, 8192);
+RINGASSERT(CAS_NRXDESC2, 32, 8192);
+RINGASSERT(CAS_NTXDESC, 32, 8192);
+
+#undef RINGASSERT
+
+#define CCDASSERT(m, a) \
+ CTASSERT((offsetof(struct cas_control_data, m) & ((a) - 1)) == 0)
+
+CCDASSERT(ccd_rxcomps, CAS_RX_COMP_ALIGN);
+CCDASSERT(ccd_rxdescs, CAS_RX_DESC_ALIGN);
+CCDASSERT(ccd_rxdescs2, CAS_RX_DESC_ALIGN);
+
+#undef CCDASSERT
-#define TRIES 10000
+#define CAS_TRIES 10000
/*
-XXXCAS
- * The hardware supports basic TCP/UDP checksum offloading. However,
- * the hardware doesn't compensate the checksum for UDP datagram which
- * can yield to 0x0. As a safe guard, UDP checksum offload is disabled
- * by default. It can be reactivated by setting special link option
- * link0 with ifconfig(8).
+ * According to documentation, the hardware has support for basic TCP
+ * checksum offloading only, in practice this can be also used for UDP
+ * however (i.e. the problem of previous Sun NICs that a checksum of 0x0
+ * was not converted to 0xffff no longer exists).
*/
-#define CAS_CSUM_FEATURES (CSUM_TCP)
+#define CAS_CSUM_FEATURES (CSUM_TCP | CSUM_UDP)
static inline void cas_add_rxdesc(struct cas_softc *sc, u_int idx);
static int cas_attach(struct cas_softc *sc);
@@ -143,10 +151,9 @@
static void cas_resume(struct cas_softc *sc);
static u_int cas_descsize(u_int sz);
static void cas_rint(struct cas_softc *sc);
-#ifdef CAS_RINT_TIMEOUT
static void cas_rint_timeout(void *arg);
-#endif
-static inline void cas_rxcksum(struct mbuf *m, uint64_t word4);
+static inline void cas_rxcksum(struct mbuf *m, uint16_t cksum);
+static inline void cas_rxcompinit(struct cas_rx_comp *rxcomp);
static u_int cas_rxcompsize(u_int sz);
static void cas_rxdma_callback(void *xsc, bus_dma_segment_t *segs,
int nsegs, int error);
@@ -157,10 +164,12 @@
static void cas_suspend(struct cas_softc *sc);
static void cas_tick(void *arg);
static void cas_tint(struct cas_softc *sc);
+static inline void cas_txkick(struct cas_softc *sc);
static int cas_watchdog(struct cas_softc *sc);
static devclass_t cas_devclass;
-DRIVER_MODULE(miibus, cas, miibus_driver, miibus_devclass, 0, 0);
+
+MODULE_DEPEND(cas, ether, 1, 1, 1);
MODULE_DEPEND(cas, miibus, 1, 1, 1);
#ifdef CAS_DEBUG
@@ -180,8 +189,6 @@
ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
if (ifp == NULL)
return (ENOSPC);
- if ((sc->sc_flags & CAS_NO_CSUM) == 0)
- sc->sc_csum_features = CAS_CSUM_FEATURES;
ifp->if_softc = sc;
if_initname(ifp, device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev));
@@ -194,9 +201,7 @@
IFQ_SET_READY(&ifp->if_snd);
callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0);
-#ifdef CAS_RINT_TIMEOUT
callout_init_mtx(&sc->sc_rx_ch, &sc->sc_mtx, 0);
-#endif
/* Make sure the chip is stopped. */
cas_reset(sc);
@@ -277,8 +282,8 @@
}
/*
- * Allocate the receive buffer, create and load the DMA maps
- * for it.
+ * Allocate the receive buffers, create and load the DMA maps
+ * for them.
*/
for (i = 0; i < CAS_NRXDESC; i++) {
if ((error = bus_dmamem_alloc(sc->sc_rdmatag,
@@ -376,11 +381,12 @@
* Tell the upper layer(s) we support long frames/checksum offloads.
*/
ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
- ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_HWCSUM;
- ifp->if_hwassist |= sc->sc_csum_features;
-#if 0
-XXXCAS ifp->if_capenable |= IFCAP_VLAN_MTU | IFCAP_HWCSUM;
-#endif
+ ifp->if_capabilities = IFCAP_VLAN_MTU;
+ if ((sc->sc_flags & CAS_NO_CSUM) == 0) {
+ ifp->if_capabilities |= IFCAP_HWCSUM;
+ ifp->if_hwassist = CAS_CSUM_FEATURES;
+ }
+ ifp->if_capenable = ifp->if_capabilities;
return (0);
@@ -431,9 +437,7 @@
cas_stop(ifp);
CAS_UNLOCK(sc);
callout_drain(&sc->sc_tick_ch);
-#ifdef CAS_RINT_TIMEOUT
callout_drain(&sc->sc_rx_ch);
-#endif
ether_ifdetach(ifp);
if_free(ifp);
device_delete_child(sc->sc_dev, sc->sc_miibus);
@@ -493,7 +497,7 @@
}
static inline void
-cas_rxcksum(struct mbuf *m, uint64_t word4)
+cas_rxcksum(struct mbuf *m, uint16_t cksum)
{
struct ether_header *eh;
struct ip *ip;
@@ -501,7 +505,6 @@
uint16_t *opts;
int32_t hlen, len, pktlen;
uint32_t temp32;
- uint16_t cksum;
pktlen = m->m_pkthdr.len;
if (pktlen < sizeof(struct ether_header) + sizeof(struct ip))
@@ -540,8 +543,7 @@
return;
}
- cksum = ~(CAS_GET(word4, CAS_RC4_TCP_CSUM));
- /* checksum fixup for IP options */
+ cksum = ~cksum;
len = hlen - sizeof(struct ip);
if (len > 0) {
opts = (uint16_t *)(ip + 1);
@@ -631,7 +633,7 @@
int i;
uint32_t reg;
- for (i = TRIES; i--; DELAY(100)) {
+ for (i = CAS_TRIES; i--; DELAY(100)) {
reg = CAS_READ_4(sc, r);
if ((reg & clr) == 0 && (reg & set) == set)
return (1);
@@ -675,13 +677,13 @@
#endif
callout_stop(&sc->sc_tick_ch);
-#ifdef CAS_RINT_TIMEOUT
callout_stop(&sc->sc_rx_ch);
-#endif
+
+ /* Disable all interrupts in order to avoid spurious ones. */
+ CAS_WRITE_4(sc, CAS_INTMASK, 0xffffffff);
- /* XXX should we reset these instead? */
- cas_disable_tx(sc);
- cas_disable_rx(sc);
+ cas_reset_tx(sc);
+ cas_reset_rx(sc);
/*
* Release any queued transmit buffers.
@@ -784,11 +786,24 @@
return (cas_bitwait(sc, CAS_MAC_TX_CONF, CAS_MAC_TX_CONF_EN, 0));
}
+static inline void
+cas_rxcompinit(struct cas_rx_comp *rxcomp)
+{
+
+ rxcomp->crc_word1 = 0;
+ rxcomp->crc_word2 = 0;
+ rxcomp->crc_word3 =
+ htole64(CAS_SET(ETHER_HDR_LEN + sizeof(struct ip), CAS_RC3_CSO));
+ rxcomp->crc_word4 = htole64(CAS_RC4_ZERO);
+}
+
static void
cas_meminit(struct cas_softc *sc)
{
int i;
+ CAS_LOCK_ASSERT(sc, MA_OWNED);
+
/*
* Initialize the transmit descriptor ring.
*/
@@ -803,12 +818,8 @@
/*
* Initialize the receive completion ring.
*/
- for (i = 0; i < CAS_NRXCOMP; i++) {
- sc->sc_rxcomps[i].crc_word1 = 0;
- sc->sc_rxcomps[i].crc_word2 = 0;
- sc->sc_rxcomps[i].crc_word3 = 0;
- sc->sc_rxcomps[i].crc_word4 = htole64(CAS_RC4_ZERO);
- }
+ for (i = 0; i < CAS_NRXCOMP; i++)
+ cas_rxcompinit(&sc->sc_rxcomps[i]);
sc->sc_rxcptr = 0;
/*
@@ -934,29 +945,21 @@
cas_setladrf(sc);
/* step 6 & 7. Program Ring Base Addresses. */
- KASSERT((CAS_CDTXDADDR(sc, 0) & (CAS_TX_DESC_ALIGN - 1)) == 0,
- ("TX descriptors incorrectly aligned"));
CAS_WRITE_4(sc, CAS_TX_DESC3_BASE_HI,
(((uint64_t)CAS_CDTXDADDR(sc, 0)) >> 32));
CAS_WRITE_4(sc, CAS_TX_DESC3_BASE_LO,
CAS_CDTXDADDR(sc, 0) & 0xffffffff);
- KASSERT((CAS_CDRXCADDR(sc, 0) & (CAS_RX_COMP_ALIGN - 1)) == 0,
- ("RX completions incorrectly aligned"));
CAS_WRITE_4(sc, CAS_RX_COMP_BASE_HI,
(((uint64_t)CAS_CDRXCADDR(sc, 0)) >> 32));
CAS_WRITE_4(sc, CAS_RX_COMP_BASE_LO,
CAS_CDRXCADDR(sc, 0) & 0xffffffff);
- KASSERT((CAS_CDRXDADDR(sc, 0) & (CAS_RX_DESC_ALIGN - 1)) == 0,
- ("RX descriptors incorrectly aligned"));
CAS_WRITE_4(sc, CAS_RX_DESC_BASE_HI,
(((uint64_t)CAS_CDRXDADDR(sc, 0)) >> 32));
CAS_WRITE_4(sc, CAS_RX_DESC_BASE_LO,
CAS_CDRXDADDR(sc, 0) & 0xffffffff);
- KASSERT((CAS_CDRXD2ADDR(sc, 0) & (CAS_RX_DESC_ALIGN - 1)) == 0,
- ("RX descriptors 2 incorrectly aligned"));
if ((sc->sc_flags & CAS_REG_PLUS) != 0) {
CAS_WRITE_4(sc, CAS_RX_DESC2_BASE_HI,
(((uint64_t)CAS_CDRXD2ADDR(sc, 0)) >> 32));
@@ -977,12 +980,15 @@
CAS_WRITE_4(sc, CAS_CAW, CAS_CAW_RR_DIS);
/*
- * Enable infinite bursts for revisions without PCI issues.
-XXXCAS
- * Doing so greatly improves RX performance.
+ * Enable infinite bursts for revisions without PCI issues if
+ * applicable. Doing so greatly improves the TX performance on
+ * !__sparc64__.
*/
CAS_WRITE_4(sc, CAS_INF_BURST,
- (sc->sc_flags & CAS_TABORT) == 0 ? CAS_INF_BURST_EN : 0);
+#if !defined(__sparc64__)
+ (sc->sc_flags & CAS_TABORT) == 0 ? CAS_INF_BURST_EN :
+#endif
+ 0);
/* Set up interrupts. */
CAS_WRITE_4(sc, CAS_INTMASK,
@@ -1037,12 +1043,6 @@
v |= cas_descsize(CAS_NRXDESC2) << CAS_RX_CONF_DESC2_SHFT;
CAS_WRITE_4(sc, CAS_RX_CONF,
v | (ETHER_ALIGN << CAS_RX_CONF_SOFF_SHFT));
-#if 0
-XXXCAS
- /* RX TCP/UDP checksum offset */
- v |= ((ETHER_HDR_LEN + sizeof(struct ip)) <<
- CAS_RX_CONFIG_CXM_START_SHFT);
-#endif
/* Set the PAUSE thresholds. We use the maximum OFF threshold. */
CAS_WRITE_4(sc, CAS_RX_PTHRS,
@@ -1133,6 +1133,8 @@
uint64_t cflags;
int error, nexttx, nsegs, offset, seg;
+ CAS_LOCK_ASSERT(sc, MA_OWNED);
+
/* Get a work queue entry. */
if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) {
/* Ran out of descriptors. */
@@ -1140,7 +1142,7 @@
}
cflags = 0;
- if (((*m_head)->m_pkthdr.csum_flags & sc->sc_csum_features) != 0) {
+ if (((*m_head)->m_pkthdr.csum_flags & CAS_CSUM_FEATURES) != 0) {
if (M_WRITABLE(*m_head) == 0) {
m = m_dup(*m_head, M_DONTWAIT);
m_freem(*m_head);
@@ -1269,6 +1271,8 @@
int i;
const u_char *laddr = IF_LLADDR(sc->sc_ifp);
+ CAS_LOCK_ASSERT(sc, MA_OWNED);
+
/* These registers are not cleared on reset. */
if ((sc->sc_flags & CAS_INITED) == 0) {
/* magic values */
@@ -1280,7 +1284,7 @@
CAS_WRITE_4(sc, CAS_MAC_MIN_FRAME, ETHER_MIN_LEN);
/* max frame length and max burst size */
CAS_WRITE_4(sc, CAS_MAC_MAX_BF,
- ((ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) <<
+ ((ETHER_MAX_LEN_JUMBO + ETHER_VLAN_ENCAP_LEN) <<
CAS_MAC_MAX_BF_FRM_SHFT) |
(0x2000 << CAS_MAC_MAX_BF_BST_SHFT));
@@ -1354,12 +1358,32 @@
CAS_UNLOCK(sc);
}
+static inline void
+cas_txkick(struct cas_softc *sc)
+{
+
+ /*
+ * Update the TX kick register. This register has to point to the
+ * descriptor after the last valid one and for optimum performance
+ * should be incremented in multiples of 4 (the DMA engine fetches/
+ * updates descriptors in batches of 4).
+ */
+#ifdef CAS_DEBUG
+ CTR3(KTR_CAS, "%s: %s: kicking TX %d",
+ device_get_name(sc->sc_dev), __func__, sc->sc_txnext);
+#endif
+ CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ CAS_WRITE_4(sc, CAS_TX_KICK3, sc->sc_txnext);
+}
+
static void
cas_start_locked(struct ifnet *ifp)
{
struct cas_softc *sc = ifp->if_softc;
struct mbuf *m;
- int ntx;
+ int kicked, ntx;
+
+ CAS_LOCK_ASSERT(sc, MA_OWNED);
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
IFF_DRV_RUNNING || (sc->sc_flags & CAS_LINK) == 0)
@@ -1371,6 +1395,7 @@
sc->sc_txnext);
#endif
ntx = 0;
+ kicked = 0;
for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->sc_txfree > 1;) {
IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
if (m == NULL)
@@ -1382,19 +1407,18 @@
IFQ_DRV_PREPEND(&ifp->if_snd, m);
break;
}
+ if ((sc->sc_txnext % 4) == 0) {
+ cas_txkick(sc);
+ kicked = 1;
+ } else
+ kicked = 0;
ntx++;
- /* Kick the transmitter. */
-#ifdef CAS_DEBUG
- CTR3(KTR_CAS, "%s: %s: kicking TX %d",
- device_get_name(sc->sc_dev), __func__, sc->sc_txnext);
-#endif
- CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
- CAS_WRITE_4(sc, CAS_TX_KICK3, sc->sc_txnext);
-
BPF_MTAP(ifp, m);
}
if (ntx > 0) {
+ if (kicked == 0)
+ cas_txkick(sc);
#ifdef CAS_DEBUG
CTR2(KTR_CAS, "%s: packets enqueued, OWN on %d",
device_get_name(sc->sc_dev), sc->sc_txnext);
@@ -1420,6 +1444,8 @@
#ifdef CAS_DEBUG
int i;
+ CAS_LOCK_ASSERT(sc, MA_OWNED);
+
CTR2(KTR_CAS, "%s: %s", device_get_name(sc->sc_dev), __func__);
#endif
@@ -1430,7 +1456,6 @@
progress = 0;
CAS_CDSYNC(sc, BUS_DMASYNC_POSTREAD);
while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) {
-
#ifdef CAS_DEBUG
if ((ifp->if_flags & IFF_DEBUG) != 0) {
printf(" txsoft %p transmit chain:\n", txs);
@@ -1511,8 +1536,8 @@
* and restart.
*/
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- sc->sc_wdog_timer = STAILQ_EMPTY(&sc->sc_txdirtyq) ? 0 : 5;
-
+ if (STAILQ_EMPTY(&sc->sc_txdirtyq))
+ sc->sc_wdog_timer = 0;
cas_start_locked(ifp);
}
@@ -1522,34 +1547,44 @@
#endif
}
-#ifdef CAS_RINT_TIMEOUT
static void
cas_rint_timeout(void *arg)
{
struct cas_softc *sc = arg;
CAS_LOCK_ASSERT(sc, MA_OWNED);
+
cas_rint(sc);
}
-#endif
static void
cas_rint(struct cas_softc *sc)
{
- struct cas_rxdsoft *rxds;
+ struct cas_rxdsoft *rxds, *rxds2;
struct ifnet *ifp = sc->sc_ifp;
- struct mbuf *m;
+ struct mbuf *m, *m2;
uint64_t word1, word2, word3, word4;
uint32_t rxhead;
- u_int i, idx, len;
+ u_int idx, idx2, len, off, skip;
+
+ CAS_LOCK_ASSERT(sc, MA_OWNED);
-#ifdef CAS_RINT_TIMEOUT
callout_stop(&sc->sc_rx_ch);
-#endif
+
#ifdef CAS_DEBUG
CTR2(KTR_CAS, "%s: %s", device_get_name(sc->sc_dev), __func__);
#endif
+#define PRINTWORD(n, delimiter) \
+ printf("word ## n: 0x%016llx%c", (long long)word ## n, delimiter)
+
+#define SKIPASSERT(n) \
+ KASSERT(sc->sc_rxcomps[sc->sc_rxcptr].crc_word ## n == 0, \
+ ("%s: word ## n not 0", __func__))
+
+#define WORDTOH(n) \
+ word ## n = le64toh(sc->sc_rxcomps[sc->sc_rxcptr].crc_word ## n)
+
/*
* Read the completion head register once. This limits
* how long the following loop can execute.
@@ -1559,27 +1594,37 @@
CTR4(KTR_CAS, "%s: sc->sc_rxcptr %d, sc->sc_rxdptr %d, head %d",
__func__, sc->rxcptr, sc->sc_rxdptr, rxhead);
#endif
+ skip = 0;
CAS_CDSYNC(sc, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
- for (i = sc->sc_rxcptr; i != rxhead; i = CAS_NEXTRXCOMP(i)) {
- word1 = le64toh(sc->sc_rxcomps[i].crc_word1);
- word2 = le64toh(sc->sc_rxcomps[i].crc_word2);
- word3 = le64toh(sc->sc_rxcomps[i].crc_word3);
- word4 = le64toh(sc->sc_rxcomps[i].crc_word4);
+ for (; sc->sc_rxcptr != rxhead;
+ sc->sc_rxcptr = CAS_NEXTRXCOMP(sc->sc_rxcptr)) {
+ if (skip != 0) {
+ SKIPASSERT(1);
+ SKIPASSERT(2);
+ SKIPASSERT(3);
+
+ --skip;
+ goto skip;
+ }
+
+ WORDTOH(1);
+ WORDTOH(2);
+ WORDTOH(3);
+ WORDTOH(4);
#ifdef CAS_DEBUG
if ((ifp->if_flags & IFF_DEBUG) != 0) {
- printf(" completion %d: ", i);
- printf("word1: 0x%016llx\t", (long long)word1);
- printf("word2: 0x%016llx\t", (long long)word2);
- printf("word3: 0x%016llx\t", (long long)word3);
- printf("word4: 0x%016llx\n", (long long)word4);
+ printf(" completion %d: ", sc->sc_rxcptr);
+ PRINTWORD(1, '\t');
+ PRINTWORD(2, '\t');
+ PRINTWORD(3, '\t');
+ PRINTWORD(4, '\n');
}
#endif
if (__predict_false(
(word1 & CAS_RC1_TYPE_MASK) == CAS_RC1_TYPE_HW ||
(word4 & CAS_RC4_ZERO) != 0)) {
-#ifdef CAS_RINT_TIMEOUT
/*
* The descriptor is still marked as owned, although
* it is supposed to have completed. This has been
@@ -1590,9 +1635,6 @@
*/
callout_reset(&sc->sc_rx_ch, CAS_RXOWN_TICKS,
cas_rint_timeout, sc);
-#endif
- device_printf(sc->sc_dev,
- "completion still owned\n");
break;
}
@@ -1604,13 +1646,25 @@
continue;
}
- len = CAS_GET(word2, CAS_RC2_HDR_SIZE);
- if (len != 0) {
+ KASSERT(CAS_GET(word1, CAS_RC1_DATA_SIZE) == 0 ||
+ CAS_GET(word2, CAS_RC2_HDR_SIZE) == 0,
+ ("%s: data and header present", __func__));
+ KASSERT((word1 & CAS_RC1_SPLIT_PKT) == 0 ||
+ CAS_GET(word2, CAS_RC2_HDR_SIZE) == 0,
+ ("%s: split and header present", __func__));
+ KASSERT(CAS_GET(word1, CAS_RC1_DATA_SIZE) == 0 ||
+ (word1 & CAS_RC1_RELEASE_HDR) == 0,
+ ("%s: data present but header release", __func__));
+ KASSERT(CAS_GET(word2, CAS_RC2_HDR_SIZE) == 0 ||
+ (word1 & CAS_RC1_RELEASE_DATA) == 0,
+ ("%s: header present but data release", __func__));
+
+ if ((len = CAS_GET(word2, CAS_RC2_HDR_SIZE)) != 0) {
idx = CAS_GET(word2, CAS_RC2_HDR_INDEX);
+ off = CAS_GET(word2, CAS_RC2_HDR_OFF);
#ifdef CAS_DEBUG
CTR4(KTR_CAS, "%s: hdr at idx %d, off %d, len %d",
- __func__, idx, (u_int)CAS_GET(word2,
- CAS_RC2_HDR_OFF), len);
+ __func__, idx, off, len);
#endif
rxds = &sc->sc_rxdsoft[idx];
MGETHDR(m, M_DONTWAIT, MT_DATA);
@@ -1619,8 +1673,7 @@
if (m != NULL) {
refcount_acquire(&rxds->rxds_refcount);
MEXTADD(m, (caddr_t)rxds->rxds_buf +
- CAS_GET(word2, CAS_RC2_HDR_OFF) * 256 +
- ETHER_ALIGN, len, cas_free,
+ off * 256 + ETHER_ALIGN, len, cas_free,
#if __FreeBSD_version < 800016
rxds,
#else
@@ -1649,16 +1702,12 @@
if ((word1 & CAS_RC1_RELEASE_HDR) != 0 &&
refcount_release(&rxds->rxds_refcount) != 0)
cas_add_rxdesc(sc, idx);
- }
-else if ((word1 & CAS_RC1_RELEASE_HDR) != 0)
-device_printf(sc->sc_dev, "unexpected header release\n");
-
- len = CAS_GET(word1, CAS_RC1_DATA_SIZE);
- if (len != 0) {
+ } else if ((len = CAS_GET(word1, CAS_RC1_DATA_SIZE)) != 0) {
idx = CAS_GET(word1, CAS_RC1_DATA_INDEX);
+ off = CAS_GET(word1, CAS_RC1_DATA_OFF);
#ifdef CAS_DEBUG
CTR4(KTR_CAS, "%s: data at idx %d, off %d, len %d",
- idx, (u_int)CAS_GET(word1, CAS_RC2_HDR_OFF), len);
+ __func__, idx, off, len);
#endif
rxds = &sc->sc_rxdsoft[idx];
MGETHDR(m, M_DONTWAIT, MT_DATA);
@@ -1666,9 +1715,10 @@
BUS_DMASYNC_POSTREAD);
if (m != NULL) {
refcount_acquire(&rxds->rxds_refcount);
- MEXTADD(m, (caddr_t)rxds->rxds_buf +
- CAS_GET(word1, CAS_RC1_DATA_OFF) +
- ETHER_ALIGN, len, cas_free,
+ off += ETHER_ALIGN;
+ m->m_len = min(CAS_PAGE_SIZE - off, len);
+ MEXTADD(m, (caddr_t)rxds->rxds_buf + off,
+ m->m_len, cas_free,
#if __FreeBSD_version < 800016
rxds,
#else
@@ -1680,9 +1730,50 @@
m = NULL;
}
}
+ idx2 = 0;
+ rxds2 = NULL;
+ if ((word1 & CAS_RC1_SPLIT_PKT) != 0) {
+ KASSERT((word1 & CAS_RC1_RELEASE_NEXT) != 0,
+ ("%s: split but no release next",
+ __func__));
+
+ idx2 = CAS_GET(word2, CAS_RC2_NEXT_INDEX);
+#ifdef CAS_DEBUG
+ CTR2(KTR_CAS, "%s: split at idx %d",
+ __func__, idx2);
+#endif
+ rxds2 = &sc->sc_rxdsoft[idx2];
+ MGET(m2, M_DONTWAIT, MT_DATA);
+ bus_dmamap_sync(sc->sc_rdmatag,
+ rxds2->rxds_dmamap,
+ BUS_DMASYNC_POSTREAD);
+ if (m2 != NULL) {
+ refcount_acquire(
+ &rxds2->rxds_refcount);
+ m2->m_len = len - m->m_len;
+ MEXTADD(m2, (caddr_t)rxds2->rxds_buf,
+ m2->m_len, cas_free,
+#if __FreeBSD_version < 800016
+ rxds2,
+#else
+ sc, (void *)(uintptr_t)idx2,
+#endif
+ 0, EXT_NET_DRV);
+ if ((m2->m_flags & M_EXT) == 0) {
+ m_freem(m2);
+ m2 = NULL;
+ }
+ }
+ if (m2 != NULL)
+ m->m_next = m2;
+ else {
+ m_freem(m);
+ m = NULL;
+ }
+ }
if (m != NULL) {
m->m_pkthdr.rcvif = ifp;
- m->m_pkthdr.len = m->m_len = len;
+ m->m_pkthdr.len = len;
ifp->if_ipackets++;
if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
cas_rxcksum(m, CAS_GET(word4,
@@ -1697,27 +1788,23 @@
if ((word1 & CAS_RC1_RELEASE_DATA) != 0 &&
refcount_release(&rxds->rxds_refcount) != 0)
cas_add_rxdesc(sc, idx);
+ if ((word1 & CAS_RC1_SPLIT_PKT) != 0 &&
+ refcount_release(&rxds2->rxds_refcount) != 0)
+ cas_add_rxdesc(sc, idx2);
}
-else if ((word1 & CAS_RC1_RELEASE_DATA) != 0)
-device_printf(sc->sc_dev, "unexpected data release\n");
-if ((word1 & CAS_RC1_SPLIT_PKT) != 0)
-device_printf(sc->sc_dev, "unexpected split packet\n");
+ skip = CAS_GET(word1, CAS_RC1_SKIP);
- i += CAS_GET(word1, CAS_RC1_SKIP);
- }
-
- for (; sc->sc_rxcptr != i;
- sc->sc_rxcptr = CAS_NEXTRXCOMP(sc->sc_rxcptr)) {
- sc->sc_rxcomps[sc->sc_rxcptr].crc_word1 = 0;
- sc->sc_rxcomps[sc->sc_rxcptr].crc_word2 = 0;
- sc->sc_rxcomps[sc->sc_rxcptr].crc_word3 = 0;
- sc->sc_rxcomps[sc->sc_rxcptr].crc_word4 =
- htole64(CAS_RC4_ZERO);
+ skip:
+ cas_rxcompinit(&sc->sc_rxcomps[sc->sc_rxcptr]);
}
CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
CAS_WRITE_4(sc, CAS_RX_COMP_TAIL, sc->sc_rxcptr);
+#undef PRINTWORD
+#undef SKIPASSERT
+#undef WORDTOH
+
#ifdef CAS_DEBUG
CTR4(KTR_CAS, "%s: done sc->sc_rxcptr %d, sc->sc_rxdptr %d, head %d",
__func__, sc->rxcptr, sc->sc_rxdptr,
@@ -1753,15 +1840,17 @@
cas_add_rxdesc(struct cas_softc *sc, u_int idx)
{
+ CAS_LOCK_ASSERT(sc, MA_OWNED);
+
CAS_UPDATE_RXDESC(sc, sc->sc_rxdptr, idx);
+ sc->sc_rxdptr = CAS_NEXTRXDESC(sc->sc_rxdptr);
/*
* Update the RX kick register. This register has to point to the
* descriptor after the last valid one (before the current batch)
- * and must be incremented in multiples of 4 (because the DMA
- * engine fetches/updates descriptors in batches of 4).
+ * and for optimum performance should be incremented in multiples
+ * of 4 (the DMA engine fetches/updates descriptors in batches of 4).
*/
- sc->sc_rxdptr = CAS_NEXTRXDESC(sc->sc_rxdptr);
if ((sc->sc_rxdptr % 4) == 0) {
CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
CAS_WRITE_4(sc, CAS_RX_KICK,
@@ -1811,24 +1900,26 @@
/*
* PCS interrupts must be cleared, otherwise no traffic is passed!
*/
- if ((status & CAS_INTR_PCS) != 0) {
+ if ((status & CAS_INTR_PCS_INT) != 0) {
status2 =
- CAS_READ_4(sc, CAS_MII_INTERRUP_STATUS) |
- CAS_READ_4(sc, CAS_MII_INTERRUP_STATUS);
- if ((status2 & CAS_MII_INTERRUP_LINK) != 0)
+ CAS_READ_4(sc, CAS_PCS_INTR_STATUS) |
+ CAS_READ_4(sc, CAS_PCS_INTR_STATUS);
+ if ((status2 & CAS_PCS_INTR_LINK) != 0)
device_printf(sc->sc_dev,
"%s: PCS link status changed\n", __func__);
}
if ((status & CAS_MAC_CTRL_STATUS) != 0) {
status2 = CAS_READ_4(sc, CAS_MAC_CTRL_STATUS);
- if ((status2 & CAS_MAC_PAUSED) != 0)
+ if ((status2 & CAS_MAC_CTRL_PAUSE) != 0)
device_printf(sc->sc_dev,
"%s: PAUSE received (PAUSE time %d slots)\n",
- __func__, CAS_MAC_PAUSE_TIME(status2));
- if ((status2 & CAS_MAC_PAUSE) != 0)
+ __func__,
+ (status2 & CAS_MAC_CTRL_STATUS_PT_MASK) >>
+ CAS_MAC_CTRL_STATUS_PT_SHFT);
+ if ((status2 & CAS_MAC_CTRL_PAUSE) != 0)
device_printf(sc->sc_dev,
"%s: transited to PAUSE state\n", __func__);
- if ((status2 & CAS_MAC_RESUME) != 0)
+ if ((status2 & CAS_MAC_CTRL_NON_PAUSE) != 0)
device_printf(sc->sc_dev,
"%s: transited to non-PAUSE state\n", __func__);
}
@@ -2078,13 +2169,17 @@
cas_mii_statchg(device_t dev)
{
struct cas_softc *sc;
+ struct ifnet *ifp;
int gigabit;
uint32_t rxcfg, txcfg, v;
sc = device_get_softc(dev);
+ ifp = sc->sc_ifp;
+ CAS_LOCK_ASSERT(sc, MA_OWNED);
+
#ifdef CAS_DEBUG
- if ((sc->sc_ifp->if_flags & IFF_DEBUG) != 0)
+ if ((ifp->if_flags & IFF_DEBUG) != 0)
device_printf(sc->sc_dev, "%s: status change: PHY = %d\n",
__func__, sc->sc_phyad);
#endif
@@ -2148,6 +2243,30 @@
#endif
CAS_WRITE_4(sc, CAS_MAC_CTRL_CONF, v);
+ /*
+ * All supported chips have a bug causing incorrect checksum
+ * to be calculated when letting them strip the FCS in half-
+ * duplex mode. In theory we could disable FCS stripping and
+ * manually adjust the checksum accordingly. It seems to make
+ * more sense to optimze for the common case and just disable
+ * hardware checksumming in half-duplex mode though.
+ */
+ if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0) {
+ ifp->if_capenable &= ~IFCAP_HWCSUM;
+ ifp->if_hwassist = 0;
+ } else if ((sc->sc_flags & CAS_NO_CSUM) == 0) {
+ ifp->if_capenable = ifp->if_capabilities;
+ ifp->if_hwassist = CAS_CSUM_FEATURES;
+ }
+
+ if (sc->sc_variant == CAS_SATURN) {
+ if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0)
+ /* silicon bug workaround */
+ CAS_WRITE_4(sc, CAS_MAC_PREAMBLE_LEN, 0x41);
+ else
+ CAS_WRITE_4(sc, CAS_MAC_PREAMBLE_LEN, 0x7);
+ }
+
if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0 &&
gigabit != 0)
CAS_WRITE_4(sc, CAS_MAC_SLOT_TIME,
@@ -2229,13 +2348,21 @@
cas_init_locked(sc);
} else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
cas_stop(ifp);
- if ((ifp->if_flags & IFF_LINK0) != 0)
- sc->sc_csum_features |= CSUM_UDP;
+ sc->sc_ifflags = ifp->if_flags;
+ CAS_UNLOCK(sc);
+ break;
+ case SIOCSIFCAP:
+ CAS_LOCK(sc);
+ if ((sc->sc_flags & CAS_NO_CSUM) != 0) {
+ error = EINVAL;
+ CAS_UNLOCK(sc);
+ break;
+ }
+ ifp->if_capenable = ifr->ifr_reqcap;
+ if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+ ifp->if_hwassist = CAS_CSUM_FEATURES;
else
- sc->sc_csum_features &= ~CSUM_UDP;
- if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
- ifp->if_hwassist = sc->sc_csum_features;
- sc->sc_ifflags = ifp->if_flags;
+ ifp->if_hwassist = 0;
CAS_UNLOCK(sc);
break;
case SIOCADDMULTI:
@@ -2244,19 +2371,17 @@
cas_setladrf(sc);
CAS_UNLOCK(sc);
break;
+ case SIOCSIFMTU:
+ if ((ifr->ifr_mtu < ETHERMIN) ||
+ (ifr->ifr_mtu > ETHERMTU_JUMBO))
+ error = EINVAL;
+ else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii->mii_media, cmd);
break;
- case SIOCSIFCAP:
- CAS_LOCK(sc);
- ifp->if_capenable = ifr->ifr_reqcap;
- if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
- ifp->if_hwassist = sc->sc_csum_features;
- else
- ifp->if_hwassist = 0;
- CAS_UNLOCK(sc);
- break;
default:
error = ether_ioctl(ifp, cmd, data);
break;
@@ -2366,7 +2491,7 @@
DEVMETHOD(miibus_writereg, cas_mii_writereg),
DEVMETHOD(miibus_statchg, cas_mii_statchg),
- { 0, 0 }
+ KOBJMETHOD_END
};
static driver_t cas_pci_driver = {
@@ -2376,8 +2501,8 @@
};
DRIVER_MODULE(cas, pci, cas_pci_driver, cas_devclass, 0, 0);
+DRIVER_MODULE(miibus, cas, miibus_driver, miibus_devclass, 0, 0);
MODULE_DEPEND(cas, pci, 1, 1, 1);
-MODULE_DEPEND(cas, ether, 1, 1, 1);
static const struct cas_pci_dev {
uint32_t cpd_devid;
@@ -2583,7 +2708,7 @@
goto fail;
}
i = 0;
- if (found >= pci_get_slot(dev))
+ if (found > 1 && pci_get_slot(dev) < sizeof(enaddr) / sizeof(*enaddr))
i = pci_get_slot(dev);
memcpy(sc->sc_enaddr, enaddr[i], ETHER_ADDR_LEN);
#endif
==== //depot/projects/usiii/dev/cas/if_casreg.h#4 (text+ko) ====
@@ -999,5 +999,6 @@
#define CAS_RC4_LEN_MMATCH 0x8000000000000000ULL /* length field mism. */
#define CAS_GET(reg, bits) (((reg) & (bits ## _MASK)) >> (bits ## _SHFT))
+#define CAS_SET(val, bits) (((val) << (bits ## _SHFT)) & (bits ## _MASK))
#endif
==== //depot/projects/usiii/dev/cas/if_casvar.h#3 (text+ko) ====
@@ -87,14 +87,8 @@
*/
struct cas_control_data {
struct cas_desc ccd_txdescs[CAS_NTXDESC]; /* TX descriptors */
- char ccd_pad0[(sizeof(struct cas_desc) * CAS_NTXDESC) %
- CAS_RX_COMP_ALIGN];
struct cas_rx_comp ccd_rxcomps[CAS_NRXCOMP]; /* RX completions */
- char ccd_pad1[(sizeof(struct cas_rx_comp) * CAS_NRXCOMP) %
- CAS_RX_DESC_ALIGN];
struct cas_desc ccd_rxdescs[CAS_NRXDESC]; /* RX descriptors */
- char ccd_pad2[(sizeof(struct cas_desc) * CAS_NRXDESC) %
- CAS_RX_DESC_ALIGN];
struct cas_desc ccd_rxdescs2[CAS_NRXDESC2]; /* RX descriptors 2 */
};
@@ -202,7 +196,6 @@
u_int sc_rxdptr; /* next ready RX descriptor */
int sc_ifflags;
- u_long sc_csum_features;
};
#define CAS_BARRIER(sc, offs, len, flags) \
@@ -228,7 +221,7 @@
#define CAS_CDSYNC(sc, ops) \
bus_dmamap_sync((sc)->sc_cdmatag, (sc)->sc_cddmamap, (ops));
-#define __CAS_UPDATE_RXDESC(rxd, rxds, s) \
+#define __CAS_UPDATE_RXDESC(rxd, rxds, s) \
do { \
\
refcount_init(&(rxds)->rxds_refcount, 1); \
@@ -246,11 +239,12 @@
#if __FreeBSD_version < 800016
#define CAS_INIT_RXDESC(sc, d, s) \
do { \
- struct cas_rxdsoft *__rxds = &(sc)->sc_rxdsoft[(s)]; \
+ struct cas_rxdsoft *__rxds; \
\
+ __rxds = &(sc)->sc_rxdsoft[(s)]; \
__rxds->rxds_sc = (sc); \
__rxds->rxds_idx = (s); \
- __CAS_UPDATE_RXDESC(&(sc)->sc_rxdescs[(d)], __rxds, (s)); \
+ __CAS_UPDATE_RXDESC(&(sc)->sc_rxdescs[(d)], __rxds, (s)); \
} while (0)
#else
#define CAS_INIT_RXDESC(sc, d, s) CAS_UPDATE_RXDESC(sc, d, s)
More information about the p4-projects
mailing list