--- if_sk.c.orig Mon Nov 14 11:56:39 2005 +++ if_sk.c Fri Jan 13 09:51:11 2006 @@ -84,30 +84,37 @@ * XMAC registers. This driver takes advantage of these features to allow * both XMACs to operate as independent interfaces. */ - + #include #include -#include +#include +#include #include #include #include #include #include +#include #include #include +#include +#include #include #include -#include #include #include #include +#include -#include +#include +#include +#include +#include +#include -#include /* for vtophys */ -#include /* for vtophys */ #include +#include #include #include #include @@ -123,7 +130,11 @@ #define SK_USEIOSPACE #endif +#if 1 #include +#else +#include "if_skreg.h" +#endif #include #include @@ -190,10 +201,17 @@ static void sk_intr_xmac(struct sk_if_softc *); static void sk_intr_bcom(struct sk_if_softc *); static void sk_intr_yukon(struct sk_if_softc *); +#if 1 +static __inline u_int16_t sk_in_addword(u_int32_t, u_int32_t); +#else +static __inline u_short sk_in_addword(u_short, u_short); +#endif +static void sk_rxcksum(struct ifnet *, struct mbuf *, u_int32_t); static void sk_rxeof(struct sk_if_softc *); +static void sk_jumbo_rxeof(struct sk_if_softc *); static void sk_txeof(struct sk_if_softc *); -static int sk_encap(struct sk_if_softc *, struct mbuf *, - u_int32_t *); +static void sk_txcksum(struct ifnet *, struct mbuf *, struct sk_tx_desc *); +static int sk_encap(struct sk_if_softc *, struct mbuf **); static void sk_start(struct ifnet *); static void sk_start_locked(struct ifnet *); static int sk_ioctl(struct ifnet *, u_long, caddr_t); @@ -206,13 +224,17 @@ static int sk_ifmedia_upd(struct ifnet *); static void sk_ifmedia_sts(struct ifnet *, struct ifmediareq *); static void sk_reset(struct sk_softc *); -static int sk_newbuf(struct sk_if_softc *, - struct sk_chain *, struct mbuf *); -static int sk_alloc_jumbo_mem(struct sk_if_softc *); -static void sk_free_jumbo_mem(struct sk_if_softc *); +static __inline void sk_discard_rxbuf(struct sk_if_softc *, int); +static __inline void sk_discard_jumbo_rxbuf(struct sk_if_softc *, int); +static int sk_newbuf(struct sk_if_softc *, int); +static int sk_jumbo_newbuf(struct sk_if_softc *, int); +static void sk_dmamap_cb(void *, bus_dma_segment_t *, int, int); +static int sk_dma_alloc(struct sk_if_softc *); +static void sk_dma_free(struct sk_if_softc *); static void *sk_jalloc(struct sk_if_softc *); static void sk_jfree(void *, void *); static int sk_init_rx_ring(struct sk_if_softc *); +static int sk_init_jumbo_rx_ring(struct sk_if_softc *); static void sk_init_tx_ring(struct sk_if_softc *); static u_int32_t sk_win_read_4(struct sk_softc *, int); static u_int16_t sk_win_read_2(struct sk_softc *, int); @@ -256,6 +278,18 @@ #endif /* + * It seems that SK-NET GENESIS supports very simple checksum offload + * capability for Tx and I believe it can generate 0 checksum value for + * UDP packets in Tx as the hardware can't differenciate UDP packets from + * TCP packets. 0 chcecksum value for UDP packet is an invalid one as it + * means sender didn't perforam checksum computation. For the safety I + * disabled UDP checksum offload capability at the moment. Alternatively + * we can intrduce a LINK0/LINK1 flag as hme(4) did it in its Tx checksum + * routine. + */ +#define SK_CSUM_FEATURES (CSUM_TCP) + +/* * Note that we have newbus methods for both the GEnesis controller * itself and the XMAC(s). The XMACs are children of the GEnesis, and * the miibus code is a child of the XMACs. We need to do it this way @@ -490,8 +524,8 @@ return; if (res.vr_id != VPD_RES_ID) { - printf("skc%d: bad VPD resource id: expected %x got %x\n", - sc->sk_unit, VPD_RES_ID, res.vr_id); + device_printf(sc->sk_dev, "bad VPD resource id: expected %x " + "got %x\n", VPD_RES_ID, res.vr_id); return; } @@ -507,8 +541,8 @@ sk_vpd_read_res(sc, &res, pos); if (res.vr_id != VPD_RES_READ) { - printf("skc%d: bad VPD resource id: expected %x got %x\n", - sc->sk_unit, VPD_RES_READ, res.vr_id); + device_printf(sc->sk_dev, "bad VPD resource id: expected %x " + "got %x\n", VPD_RES_READ, res.vr_id); return; } @@ -595,7 +629,6 @@ if (sc_if->sk_phytype == SK_PHYTYPE_XMAC && phy != 0) return(0); - SK_IF_LOCK(sc_if); SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg|(phy << 8)); SK_XM_READ_2(sc_if, XM_PHY_DATA); if (sc_if->sk_phytype != SK_PHYTYPE_XMAC) { @@ -607,15 +640,13 @@ } if (i == SK_TIMEOUT) { - printf("sk%d: phy failed to come ready\n", - sc_if->sk_unit); - SK_IF_UNLOCK(sc_if); + if_printf(sc_if->sk_ifp, "phy failed to come ready\n"); return(0); } } DELAY(1); i = SK_XM_READ_2(sc_if, XM_PHY_DATA); - SK_IF_UNLOCK(sc_if); + return(i); } @@ -626,7 +657,6 @@ { int i; - SK_IF_LOCK(sc_if); SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg|(phy << 8)); for (i = 0; i < SK_TIMEOUT; i++) { if (!(SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYBUSY)) @@ -634,9 +664,8 @@ } if (i == SK_TIMEOUT) { - printf("sk%d: phy failed to come ready\n", sc_if->sk_unit); - SK_IF_UNLOCK(sc_if); - return(ETIMEDOUT); + if_printf(sc_if->sk_ifp, "phy failed to come ready\n"); + return (ETIMEDOUT); } SK_XM_WRITE_2(sc_if, XM_PHY_DATA, val); @@ -645,9 +674,8 @@ if (!(SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYBUSY)) break; } - SK_IF_UNLOCK(sc_if); if (i == SK_TIMEOUT) - printf("sk%d: phy write timed out\n", sc_if->sk_unit); + if_printf(sc_if->sk_ifp, "phy write timed out\n"); return(0); } @@ -660,7 +688,6 @@ mii = device_get_softc(sc_if->sk_miibus); - SK_IF_LOCK(sc_if); /* * If this is a GMII PHY, manually set the XMAC's * duplex mode accordingly. @@ -672,9 +699,6 @@ SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_GMIIFDX); } } - SK_IF_UNLOCK(sc_if); - - return; } static int @@ -691,10 +715,9 @@ return(0); } - SK_IF_LOCK(sc_if); SK_YU_WRITE_2(sc_if, YUKON_SMICR, YU_SMICR_PHYAD(phy) | YU_SMICR_REGAD(reg) | YU_SMICR_OP_READ); - + for (i = 0; i < SK_TIMEOUT; i++) { DELAY(1); val = SK_YU_READ_2(sc_if, YUKON_SMICR); @@ -703,14 +726,11 @@ } if (i == SK_TIMEOUT) { - printf("sk%d: phy failed to come ready\n", - sc_if->sk_unit); - SK_IF_UNLOCK(sc_if); + if_printf(sc_if->sk_ifp, "phy failed to come ready\n"); return(0); } - + val = SK_YU_READ_2(sc_if, YUKON_SMIDR); - SK_IF_UNLOCK(sc_if); return(val); } @@ -722,7 +742,6 @@ { int i; - SK_IF_LOCK(sc_if); SK_YU_WRITE_2(sc_if, YUKON_SMIDR, val); SK_YU_WRITE_2(sc_if, YUKON_SMICR, YU_SMICR_PHYAD(phy) | YU_SMICR_REGAD(reg) | YU_SMICR_OP_WRITE); @@ -732,7 +751,10 @@ if (SK_YU_READ_2(sc_if, YUKON_SMICR) & YU_SMICR_BUSY) break; } - SK_IF_UNLOCK(sc_if); + if (i == SK_TIMEOUT) { + if_printf(sc_if->sk_ifp, "phy write timeout out\n"); + return (0); + } return(0); } @@ -798,6 +820,7 @@ int h = 0, i; struct ifmultiaddr *ifma; u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; + u_int8_t maddr[ETHER_ADDR_LEN]; SK_IF_LOCK_ASSERT(sc_if); @@ -836,22 +859,28 @@ * use the hash table. */ if (sc->sk_type == SK_GENESIS && i < XM_RXFILT_MAX) { - sk_setfilt(sc_if, - LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); + bcopy(LLADDR( + (struct sockaddr_dl *)ifma->ifma_addr), + maddr, ETHER_ADDR_LEN); + sk_setfilt(sc_if, maddr, i); i++; continue; } switch(sc->sk_type) { case SK_GENESIS: - h = sk_xmchash( - LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + bcopy(LLADDR( + (struct sockaddr_dl *)ifma->ifma_addr), + maddr, ETHER_ADDR_LEN); + h = sk_xmchash(maddr); break; case SK_YUKON: case SK_YUKON_LITE: case SK_YUKON_LP: - h = sk_gmchash( - LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + bcopy(LLADDR( + (struct sockaddr_dl *)ifma->ifma_addr), + maddr, ETHER_ADDR_LEN); + h = sk_gmchash(maddr); break; } if (h < 32) @@ -919,279 +948,240 @@ sk_init_rx_ring(sc_if) struct sk_if_softc *sc_if; { - struct sk_chain_data *cd = &sc_if->sk_cdata; - struct sk_ring_data *rd = sc_if->sk_rdata; + struct sk_ring_data *rd; + bus_addr_t addr; + u_int32_t csum_start; int i; - bzero((char *)rd->sk_rx_ring, - sizeof(struct sk_rx_desc) * SK_RX_RING_CNT); + sc_if->sk_cdata.sk_rx_prod = 0; + csum_start = (ETHER_HDR_LEN + sizeof(struct ip)) << 16 | + ETHER_HDR_LEN; + rd = &sc_if->sk_rdata; + bzero(rd->sk_rx_ring, sizeof(struct sk_rx_desc) * SK_RX_RING_CNT); for (i = 0; i < SK_RX_RING_CNT; i++) { - cd->sk_rx_chain[i].sk_desc = &rd->sk_rx_ring[i]; - if (sk_newbuf(sc_if, &cd->sk_rx_chain[i], NULL) == ENOBUFS) - return(ENOBUFS); - if (i == (SK_RX_RING_CNT - 1)) { - cd->sk_rx_chain[i].sk_next = - &cd->sk_rx_chain[0]; - rd->sk_rx_ring[i].sk_next = - vtophys(&rd->sk_rx_ring[0]); - } else { - cd->sk_rx_chain[i].sk_next = - &cd->sk_rx_chain[i + 1]; - rd->sk_rx_ring[i].sk_next = - vtophys(&rd->sk_rx_ring[i + 1]); - } + if (sk_newbuf(sc_if, i) != 0) + return (ENOBUFS); + if (i == (SK_RX_RING_CNT - 1)) + addr = SK_RX_RING_ADDR(sc_if, 0); + else + addr = SK_RX_RING_ADDR(sc_if, i + 1); + rd->sk_rx_ring[i].sk_next = htole32(SK_ADDR_LO(addr)); + rd->sk_rx_ring[i].sk_csum_start = htole32(csum_start); } - sc_if->sk_cdata.sk_rx_prod = 0; - sc_if->sk_cdata.sk_rx_cons = 0; + bus_dmamap_sync(sc_if->sk_cdata.sk_rx_ring_tag, + sc_if->sk_cdata.sk_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return(0); } -static void -sk_init_tx_ring(sc_if) +static int +sk_init_jumbo_rx_ring(sc_if) struct sk_if_softc *sc_if; { - struct sk_chain_data *cd = &sc_if->sk_cdata; - struct sk_ring_data *rd = sc_if->sk_rdata; + struct sk_ring_data *rd; + bus_addr_t addr; + u_int32_t csum_start; int i; - bzero((char *)sc_if->sk_rdata->sk_tx_ring, - sizeof(struct sk_tx_desc) * SK_TX_RING_CNT); + sc_if->sk_cdata.sk_jumbo_rx_prod = 0; - for (i = 0; i < SK_TX_RING_CNT; i++) { - cd->sk_tx_chain[i].sk_desc = &rd->sk_tx_ring[i]; - if (i == (SK_TX_RING_CNT - 1)) { - cd->sk_tx_chain[i].sk_next = - &cd->sk_tx_chain[0]; - rd->sk_tx_ring[i].sk_next = - vtophys(&rd->sk_tx_ring[0]); - } else { - cd->sk_tx_chain[i].sk_next = - &cd->sk_tx_chain[i + 1]; - rd->sk_tx_ring[i].sk_next = - vtophys(&rd->sk_tx_ring[i + 1]); - } + csum_start = ((ETHER_HDR_LEN + sizeof(struct ip)) << 16) | + ETHER_HDR_LEN; + rd = &sc_if->sk_rdata; + bzero(rd->sk_jumbo_rx_ring, + sizeof(struct sk_rx_desc) * SK_JUMBO_RX_RING_CNT); + for (i = 0; i < SK_JUMBO_RX_RING_CNT; i++) { + if (sk_jumbo_newbuf(sc_if, i) != 0) + return (ENOBUFS); + if (i == (SK_JUMBO_RX_RING_CNT - 1)) + addr = SK_JUMBO_RX_RING_ADDR(sc_if, 0); + else + addr = SK_JUMBO_RX_RING_ADDR(sc_if, i + 1); + rd->sk_jumbo_rx_ring[i].sk_next = htole32(SK_ADDR_LO(addr)); + rd->sk_jumbo_rx_ring[i].sk_csum_start = htole32(csum_start); } - sc_if->sk_cdata.sk_tx_prod = 0; - sc_if->sk_cdata.sk_tx_cons = 0; - sc_if->sk_cdata.sk_tx_cnt = 0; + bus_dmamap_sync(sc_if->sk_cdata.sk_jumbo_rx_ring_tag, + sc_if->sk_cdata.sk_jumbo_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); - return; + return (0); } -static int -sk_newbuf(sc_if, c, m) +static void +sk_init_tx_ring(sc_if) struct sk_if_softc *sc_if; - struct sk_chain *c; - struct mbuf *m; { - struct mbuf *m_new = NULL; - struct sk_rx_desc *r; + struct sk_ring_data *rd; + struct sk_txdesc *txd; + bus_addr_t addr; + int i; - if (m == NULL) { - caddr_t *buf = NULL; + STAILQ_INIT(&sc_if->sk_cdata.sk_txfreeq); + STAILQ_INIT(&sc_if->sk_cdata.sk_txbusyq); - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) - return(ENOBUFS); - - /* Allocate the jumbo buffer */ - buf = sk_jalloc(sc_if); - if (buf == NULL) { - m_freem(m_new); -#ifdef SK_VERBOSE - printf("sk%d: jumbo allocation failed " - "-- packet dropped!\n", sc_if->sk_unit); -#endif - return(ENOBUFS); - } + sc_if->sk_cdata.sk_tx_prod = 0; + sc_if->sk_cdata.sk_tx_cons = 0; + sc_if->sk_cdata.sk_tx_cnt = 0; - /* Attach the buffer to the mbuf */ - MEXTADD(m_new, buf, SK_JLEN, sk_jfree, - (struct sk_if_softc *)sc_if, 0, EXT_NET_DRV); - m_new->m_data = (void *)buf; - m_new->m_pkthdr.len = m_new->m_len = SK_JLEN; - } else { - /* - * We're re-using a previously allocated mbuf; - * be sure to re-init pointers and lengths to - * default values. - */ - m_new = m; - m_new->m_len = m_new->m_pkthdr.len = SK_JLEN; - m_new->m_data = m_new->m_ext.ext_buf; + rd = &sc_if->sk_rdata; + bzero(rd->sk_tx_ring, sizeof(struct sk_tx_desc) * SK_TX_RING_CNT); + for (i = 0; i < SK_TX_RING_CNT; i++) { + if (i == (SK_TX_RING_CNT - 1)) + addr = SK_TX_RING_ADDR(sc_if, 0); + else + addr = SK_TX_RING_ADDR(sc_if, i + 1); + rd->sk_tx_ring[i].sk_next = htole32(SK_ADDR_LO(addr)); + txd = &sc_if->sk_cdata.sk_txdesc[i]; + STAILQ_INSERT_TAIL(&sc_if->sk_cdata.sk_txfreeq, txd, tx_q); } - /* - * Adjust alignment so packet payload begins on a - * longword boundary. Mandatory for Alpha, useful on - * x86 too. - */ - m_adj(m_new, ETHER_ALIGN); - - r = c->sk_desc; - c->sk_mbuf = m_new; - r->sk_data_lo = vtophys(mtod(m_new, caddr_t)); - r->sk_ctl = m_new->m_len | SK_RXSTAT; - - return(0); + bus_dmamap_sync(sc_if->sk_cdata.sk_tx_ring_tag, + sc_if->sk_cdata.sk_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } -/* - * Allocate jumbo buffer storage. The SysKonnect adapters support - * "jumbograms" (9K frames), although SysKonnect doesn't currently - * use them in their drivers. In order for us to use them, we need - * large 9K receive buffers, however standard mbuf clusters are only - * 2048 bytes in size. Consequently, we need to allocate and manage - * our own jumbo buffer pool. Fortunately, this does not require an - * excessive amount of additional code. - */ -static int -sk_alloc_jumbo_mem(sc_if) +static __inline void +sk_discard_rxbuf(sc_if, idx) struct sk_if_softc *sc_if; + int idx; { - caddr_t ptr; - register int i; - struct sk_jpool_entry *entry; - - /* Grab a big chunk o' storage. */ - sc_if->sk_cdata.sk_jumbo_buf = contigmalloc(SK_JMEM, M_DEVBUF, - M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); - - if (sc_if->sk_cdata.sk_jumbo_buf == NULL) { - printf("sk%d: no memory for jumbo buffers!\n", sc_if->sk_unit); - return(ENOBUFS); - } - - mtx_init(&sc_if->sk_jlist_mtx, "sk_jlist_mtx", NULL, MTX_DEF); - - SLIST_INIT(&sc_if->sk_jfree_listhead); - SLIST_INIT(&sc_if->sk_jinuse_listhead); + struct sk_rx_desc *r; + struct sk_rxdesc *rxd; + struct mbuf *m; - /* - * Now divide it up into 9K pieces and save the addresses - * in an array. - */ - ptr = sc_if->sk_cdata.sk_jumbo_buf; - for (i = 0; i < SK_JSLOTS; i++) { - sc_if->sk_cdata.sk_jslots[i] = ptr; - ptr += SK_JLEN; - entry = malloc(sizeof(struct sk_jpool_entry), - M_DEVBUF, M_NOWAIT); - if (entry == NULL) { - sk_free_jumbo_mem(sc_if); - sc_if->sk_cdata.sk_jumbo_buf = NULL; - printf("sk%d: no memory for jumbo " - "buffer queue!\n", sc_if->sk_unit); - return(ENOBUFS); - } - entry->slot = i; - SLIST_INSERT_HEAD(&sc_if->sk_jfree_listhead, - entry, jpool_entries); - } - return(0); + r = &sc_if->sk_rdata.sk_rx_ring[idx]; + rxd = &sc_if->sk_cdata.sk_rxdesc[idx]; + m = rxd->rx_m; + r->sk_ctl = htole32(m->m_len | SK_CRXSTAT); } -static void -sk_free_jumbo_mem(sc_if) +static __inline void +sk_discard_jumbo_rxbuf(sc_if, idx) struct sk_if_softc *sc_if; + int idx; { - struct sk_jpool_entry *entry; - - SK_JLIST_LOCK(sc_if); - - /* We cannot release external mbuf storage while in use. */ - if (!SLIST_EMPTY(&sc_if->sk_jinuse_listhead)) { - printf("sk%d: will leak jumbo buffer memory!\n", sc_if->sk_unit); - SK_JLIST_UNLOCK(sc_if); - return; - } - - while (!SLIST_EMPTY(&sc_if->sk_jfree_listhead)) { - entry = SLIST_FIRST(&sc_if->sk_jfree_listhead); - SLIST_REMOVE_HEAD(&sc_if->sk_jfree_listhead, jpool_entries); - free(entry, M_DEVBUF); - } - - SK_JLIST_UNLOCK(sc_if); - - mtx_destroy(&sc_if->sk_jlist_mtx); - - contigfree(sc_if->sk_cdata.sk_jumbo_buf, SK_JMEM, M_DEVBUF); + struct sk_rx_desc *r; + struct sk_rxdesc *rxd; + struct mbuf *m; - return; + r = &sc_if->sk_rdata.sk_jumbo_rx_ring[idx]; + rxd = &sc_if->sk_cdata.sk_jumbo_rxdesc[idx]; + m = rxd->rx_m; + r->sk_ctl = htole32(m->m_len | SK_CRXSTAT); } -/* - * Allocate a jumbo buffer. - */ -static void * -sk_jalloc(sc_if) +static int +sk_newbuf(sc_if, idx) struct sk_if_softc *sc_if; + int idx; { - struct sk_jpool_entry *entry; - - SK_JLIST_LOCK(sc_if); - - entry = SLIST_FIRST(&sc_if->sk_jfree_listhead); - - if (entry == NULL) { -#ifdef SK_VERBOSE - printf("sk%d: no free jumbo buffers\n", sc_if->sk_unit); -#endif - SK_JLIST_UNLOCK(sc_if); - return(NULL); - } - - SLIST_REMOVE_HEAD(&sc_if->sk_jfree_listhead, jpool_entries); - SLIST_INSERT_HEAD(&sc_if->sk_jinuse_listhead, entry, jpool_entries); - - SK_JLIST_UNLOCK(sc_if); + struct sk_rx_desc *r; + struct sk_rxdesc *rxd; + struct mbuf *m; + bus_dma_segment_t segs[1]; + bus_dmamap_t map; + int nsegs; + + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + return (ENOBUFS); + m->m_len = m->m_pkthdr.len = MCLBYTES; + m_adj(m, ETHER_ALIGN); + + if (bus_dmamap_load_mbuf_sg(sc_if->sk_cdata.sk_rx_tag, + sc_if->sk_cdata.sk_rx_sparemap, m, segs, &nsegs, 0) != 0) { + m_freem(m); + return (ENOBUFS); + } + KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); + + rxd = &sc_if->sk_cdata.sk_rxdesc[idx]; + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc_if->sk_cdata.sk_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc_if->sk_cdata.sk_rx_tag, rxd->rx_dmamap); + } + map = rxd->rx_dmamap; + rxd->rx_dmamap = sc_if->sk_cdata.sk_rx_sparemap; + sc_if->sk_cdata.sk_rx_sparemap = map; + bus_dmamap_sync(sc_if->sk_cdata.sk_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_PREREAD); + rxd->rx_m = m; + r = &sc_if->sk_rdata.sk_rx_ring[idx]; + r->sk_data_lo = htole32(SK_ADDR_LO(segs[0].ds_addr)); + r->sk_data_hi = htole32(SK_ADDR_HI(segs[0].ds_addr)); + r->sk_ctl = htole32(segs[0].ds_len | SK_CRXSTAT); - return(sc_if->sk_cdata.sk_jslots[entry->slot]); + return (0); } -/* - * Release a jumbo buffer. - */ -static void -sk_jfree(buf, args) - void *buf; - void *args; -{ +static int +sk_jumbo_newbuf(sc_if, idx) struct sk_if_softc *sc_if; - int i; - struct sk_jpool_entry *entry; - - /* Extract the softc struct pointer. */ - sc_if = (struct sk_if_softc *)args; - if (sc_if == NULL) - panic("sk_jfree: didn't get softc pointer!"); - - SK_JLIST_LOCK(sc_if); - - /* calculate the slot this buffer belongs to */ - i = ((vm_offset_t)buf - - (vm_offset_t)sc_if->sk_cdata.sk_jumbo_buf) / SK_JLEN; - - if ((i < 0) || (i >= SK_JSLOTS)) - panic("sk_jfree: asked to free buffer that we don't manage!"); + int idx; +{ + struct sk_rx_desc *r; + struct sk_rxdesc *rxd; + struct mbuf *m; + bus_dma_segment_t segs[1]; + bus_dmamap_t map; + int nsegs; + caddr_t buf; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (ENOBUFS); + buf = sk_jalloc(sc_if); + if (buf == NULL) { + m_freem(m); + return (ENOBUFS); + } + /* Attach the buffer to the mbuf */ + MEXTADD(m, buf, SK_JLEN, sk_jfree, (struct sk_if_softc *)sc_if, 0, + EXT_NET_DRV); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return (ENOBUFS); + } + m->m_pkthdr.len = m->m_len = SK_JLEN; + /* + * Adjust alignment so packet payload begins on a + * longword boundary. Mandatory for Alpha, useful on + * x86 too. + */ + m_adj(m, ETHER_ALIGN); - entry = SLIST_FIRST(&sc_if->sk_jinuse_listhead); - if (entry == NULL) - panic("sk_jfree: buffer not in use!"); - entry->slot = i; - SLIST_REMOVE_HEAD(&sc_if->sk_jinuse_listhead, jpool_entries); - SLIST_INSERT_HEAD(&sc_if->sk_jfree_listhead, entry, jpool_entries); - if (SLIST_EMPTY(&sc_if->sk_jinuse_listhead)) - wakeup(sc_if); + if (bus_dmamap_load_mbuf_sg(sc_if->sk_cdata.sk_jumbo_rx_tag, + sc_if->sk_cdata.sk_jumbo_rx_sparemap, m, segs, &nsegs, 0) != 0) { + m_freem(m); + return (ENOBUFS); + } + KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); + + rxd = &sc_if->sk_cdata.sk_jumbo_rxdesc[idx]; + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc_if->sk_cdata.sk_jumbo_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc_if->sk_cdata.sk_jumbo_rx_tag, + rxd->rx_dmamap); + } + map = rxd->rx_dmamap; + rxd->rx_dmamap = sc_if->sk_cdata.sk_jumbo_rx_sparemap; + sc_if->sk_cdata.sk_jumbo_rx_sparemap = map; + bus_dmamap_sync(sc_if->sk_cdata.sk_jumbo_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_PREREAD); + rxd->rx_m = m; + r = &sc_if->sk_rdata.sk_jumbo_rx_ring[idx]; + r->sk_data_lo = htole32(SK_ADDR_LO(segs[0].ds_addr)); + r->sk_data_hi = htole32(SK_ADDR_HI(segs[0].ds_addr)); + r->sk_ctl = htole32(segs[0].ds_len | SK_CRXSTAT); - SK_JLIST_UNLOCK(sc_if); - return; + return (0); } /* @@ -1240,9 +1230,10 @@ { struct sk_if_softc *sc_if = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; - int error = 0; + int error, mask; struct mii_data *mii; + error = 0; switch(command) { case SIOCSIFMTU: SK_IF_LOCK(sc_if); @@ -1272,15 +1263,12 @@ } sc_if->sk_if_flags = ifp->if_flags; SK_IF_UNLOCK(sc_if); - error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: SK_IF_LOCK(sc_if); - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) sk_setmulti(sc_if); - error = 0; - } SK_IF_UNLOCK(sc_if); break; case SIOCGIFMEDIA: @@ -1288,6 +1276,19 @@ mii = device_get_softc(sc_if->sk_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; + case SIOCSIFCAP: + SK_IF_LOCK(sc_if); + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if (mask & IFCAP_HWCSUM) { + ifp->if_capenable ^= IFCAP_HWCSUM; + if (IFCAP_HWCSUM & ifp->if_capenable && + IFCAP_HWCSUM & ifp->if_capabilities) + ifp->if_hwassist = SK_CSUM_FEATURES; + else + ifp->if_hwassist = 0; + } + SK_IF_UNLOCK(sc_if); + break; default: error = ether_ioctl(ifp, command, data); break; @@ -1339,6 +1340,7 @@ sk_reset(sc) struct sk_softc *sc; { + CSR_WRITE_2(sc, SK_CSR, SK_CSR_SW_RESET); CSR_WRITE_2(sc, SK_CSR, SK_CSR_MASTER_RESET); if (SK_YUKON_FAMILY(sc->sk_type)) @@ -1368,13 +1370,23 @@ * defers interrupts specified in the interrupt moderation * timer mask based on the timeout specified in the interrupt * moderation timer init register. Each bit in the timer - * register represents 18.825ns, so to specify a timeout in - * microseconds, we have to multiply by 54. + * register represents one tick, so to specify a timeout in + * microseconds, we have to multiply by the correct number of + * ticks-per-microsecond. */ + switch (sc->sk_type) { + case SK_GENESIS: + sc->sk_int_ticks = SK_IMTIMER_TICKS_GENESIS; + break; + default: + sc->sk_int_ticks = SK_IMTIMER_TICKS_YUKON; + break; + } if (bootverbose) - printf("skc%d: interrupt moderation is %d us\n", - sc->sk_unit, sc->sk_int_mod); - sk_win_write_4(sc, SK_IMTIMERINIT, SK_IM_USECS(sc->sk_int_mod)); + device_printf(sc->sk_dev, "interrupt moderation is %d us\n", + sc->sk_int_mod); + sk_win_write_4(sc, SK_IMTIMERINIT, SK_IM_USECS(sc->sk_int_mod, + sc->sk_int_ticks)); sk_win_write_4(sc, SK_IMMR, SK_ISR_TX1_S_EOF|SK_ISR_TX2_S_EOF| SK_ISR_RX1_EOF|SK_ISR_RX2_EOF); sk_win_write_1(sc, SK_IMTIMERCTL, SK_IMCTL_START); @@ -1432,8 +1444,7 @@ sc = device_get_softc(device_get_parent(dev)); port = *(int *)device_get_ivars(dev); - sc_if->sk_dev = dev; - sc_if->sk_unit = device_get_unit(dev); + sc_if->sk_if_dev = dev; sc_if->sk_port = port; sc_if->sk_softc = sc; sc->sk_if[port] = sc_if; @@ -1442,27 +1453,16 @@ if (port == SK_PORT_B) sc_if->sk_tx_bmu = SK_BMU_TXS_CSR1; - /* Allocate the descriptor queues. */ - sc_if->sk_rdata = contigmalloc(sizeof(struct sk_ring_data), M_DEVBUF, - M_NOWAIT, M_ZERO, 0xffffffff, PAGE_SIZE, 0); - - if (sc_if->sk_rdata == NULL) { - printf("sk%d: no memory for list buffers!\n", sc_if->sk_unit); - error = ENOMEM; - goto fail; - } + callout_init_mtx(&sc_if->sk_tick_ch, &sc_if->sk_softc->sk_mtx, 0); - /* Try to allocate memory for jumbo buffers. */ - if (sk_alloc_jumbo_mem(sc_if)) { - printf("sk%d: jumbo buffer allocation failed\n", - sc_if->sk_unit); + if (sk_dma_alloc(sc_if) != 0) { error = ENOMEM; goto fail; } ifp = sc_if->sk_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { - printf("sk%d: can not if_alloc()\n", sc_if->sk_unit); + device_printf(sc_if->sk_if_dev, "can not if_alloc()\n"); error = ENOSPC; goto fail; } @@ -1475,18 +1475,18 @@ * XMAC II has 0x8100 in VLAN Tag Level 1 register initially; * YU_SMR_MFL_VLAN is set by this driver in Yukon. */ - ifp->if_capabilities = ifp->if_capenable = IFCAP_VLAN_MTU; + ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM; + ifp->if_capenable = ifp->if_capabilities; + ifp->if_hwassist = SK_CSUM_FEATURES; ifp->if_ioctl = sk_ioctl; ifp->if_start = sk_start; ifp->if_watchdog = sk_watchdog; ifp->if_init = sk_init; - ifp->if_baudrate = 1000000000; + ifp->if_baudrate = IF_Mbps(1000); IFQ_SET_MAXLEN(&ifp->if_snd, SK_TX_RING_CNT - 1); ifp->if_snd.ifq_drv_maxlen = SK_TX_RING_CNT - 1; IFQ_SET_READY(&ifp->if_snd); - callout_handle_init(&sc_if->sk_tick_ch); - /* * Get station address for this interface. Note that * dual port cards actually come with three station @@ -1547,8 +1547,8 @@ sc_if->sk_phyaddr = SK_PHYADDR_MARV; break; default: - printf("skc%d: unsupported PHY type: %d\n", - sc->sk_unit, sc_if->sk_phytype); + device_printf(sc->sk_dev, "unsupported PHY type: %d\n", + sc_if->sk_phytype); error = ENODEV; SK_UNLOCK(sc); goto fail; @@ -1579,7 +1579,7 @@ SK_UNLOCK(sc); if (mii_phy_probe(dev, &sc_if->sk_miibus, sk_ifmedia_upd, sk_ifmedia_sts)) { - printf("skc%d: no PHY found!\n", sc_if->sk_unit); + device_printf(sc_if->sk_if_dev, "no PHY found!\n"); ether_ifdetach(ifp); error = ENXIO; goto fail; @@ -1604,15 +1604,15 @@ device_t dev; { struct sk_softc *sc; - int unit, error = 0, rid, *port; + int error = 0, rid, *port; uint8_t skrs; char *pname, *revstr; sc = device_get_softc(dev); - unit = device_get_unit(dev); + sc->sk_dev = dev; mtx_init(&sc->sk_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, - MTX_DEF | MTX_RECURSE); + MTX_DEF); /* * Map control/status registers. */ @@ -1622,7 +1622,7 @@ sc->sk_res = bus_alloc_resource_any(dev, SK_RES, &rid, RF_ACTIVE); if (sc->sk_res == NULL) { - printf("sk%d: couldn't map ports/memory\n", unit); + device_printf(dev, "couldn't map ports/memory\n"); error = ENXIO; goto fail; } @@ -1635,8 +1635,8 @@ /* Bail out if chip is not recognized. */ if (sc->sk_type != SK_GENESIS && !SK_YUKON_FAMILY(sc->sk_type)) { - printf("skc%d: unknown device: chipver=%02x, rev=%x\n", - unit, sc->sk_type, sc->sk_rev); + device_printf(dev, "unknown device: chipver=%02x, rev=%x\n", + sc->sk_type, sc->sk_rev); error = ENXIO; goto fail; } @@ -1647,7 +1647,7 @@ RF_SHAREABLE | RF_ACTIVE); if (sc->sk_irq == NULL) { - printf("skc%d: couldn't map interrupt\n", unit); + device_printf(dev, "couldn't map interrupt\n"); error = ENXIO; goto fail; } @@ -1660,13 +1660,13 @@ /* Pull in device tunables. */ sc->sk_int_mod = SK_IM_DEFAULT; - error = resource_int_value(device_get_name(dev), unit, + error = resource_int_value(device_get_name(dev), device_get_unit(dev), "int_mod", &sc->sk_int_mod); if (error == 0) { if (sc->sk_int_mod < SK_IM_MIN || sc->sk_int_mod > SK_IM_MAX) { - printf("skc%d: int_mod value out of range; " - "using default: %d\n", unit, SK_IM_DEFAULT); + device_printf(dev, "int_mod value out of range; " + "using default: %d\n", SK_IM_DEFAULT); sc->sk_int_mod = SK_IM_DEFAULT; } } @@ -1674,8 +1674,6 @@ /* Reset the adapter. */ sk_reset(sc); - sc->sk_unit = unit; - /* Read and save vital product data from EEPROM. */ sk_vpd_read(sc); @@ -1700,8 +1698,7 @@ sc->sk_rboff = SK_RBOFF_0; break; default: - printf("skc%d: unknown ram size: %d\n", - sc->sk_unit, skrs); + device_printf(dev, "unknown ram size: %d\n", skrs); error = ENXIO; goto fail; } @@ -1728,8 +1725,8 @@ sc->sk_pmd = IFM_1000_T; break; default: - printf("skc%d: unknown media type: 0x%x\n", - sc->sk_unit, sk_win_read_1(sc, SK_PMDTYPE)); + device_printf(dev, "unknown media type: 0x%x\n", + sk_win_read_1(sc, SK_PMDTYPE)); error = ENXIO; goto fail; } @@ -1900,7 +1897,7 @@ sk_intr, sc, &sc->sk_intrhand); if (error) { - printf("skc%d: couldn't set up irq\n", unit); + device_printf(dev, "couldn't set up irq\n"); goto fail; } @@ -1936,6 +1933,7 @@ sk_stop(sc_if); /* Can't hold locks while calling detach */ SK_IF_UNLOCK(sc_if); + callout_drain(&sc_if->sk_tick_ch); ether_ifdetach(ifp); SK_IF_LOCK(sc_if); } @@ -1951,12 +1949,7 @@ device_delete_child(dev, sc_if->sk_miibus); */ bus_generic_detach(dev); - if (sc_if->sk_cdata.sk_jumbo_buf != NULL) - sk_free_jumbo_mem(sc_if); - if (sc_if->sk_rdata != NULL) { - contigfree(sc_if->sk_rdata, sizeof(struct sk_ring_data), - M_DEVBUF); - } + sk_dma_free(sc_if); SK_IF_UNLOCK(sc_if); return(0); @@ -2000,57 +1993,696 @@ return(0); } +struct sk_dmamap_arg { + bus_addr_t sk_busaddr; +}; + +static void +sk_dmamap_cb(arg, segs, nseg, error) + void *arg; + bus_dma_segment_t *segs; + int nseg; + int error; +{ + struct sk_dmamap_arg *ctx; + + if (error != 0) + return; + + ctx = arg; + ctx->sk_busaddr = segs[0].ds_addr; +} + +/* + * Allocate jumbo buffer storage. The SysKonnect adapters support + * "jumbograms" (9K frames), although SysKonnect doesn't currently + * use them in their drivers. In order for us to use them, we need + * large 9K receive buffers, however standard mbuf clusters are only + * 2048 bytes in size. Consequently, we need to allocate and manage + * our own jumbo buffer pool. Fortunately, this does not require an + * excessive amount of additional code. + */ static int -sk_encap(sc_if, m_head, txidx) - struct sk_if_softc *sc_if; - struct mbuf *m_head; - u_int32_t *txidx; +sk_dma_alloc(sc_if) + struct sk_if_softc *sc_if; { - struct sk_tx_desc *f = NULL; - struct mbuf *m; - u_int32_t frag, cur, cnt = 0; + struct sk_dmamap_arg ctx; + struct sk_txdesc *txd; + struct sk_rxdesc *rxd; + struct sk_rxdesc *jrxd; + caddr_t ptr; + struct sk_jpool_entry *entry; + int error, i; - SK_IF_LOCK_ASSERT(sc_if); + mtx_init(&sc_if->sk_jlist_mtx, "sk_jlist_mtx", NULL, MTX_DEF); + SLIST_INIT(&sc_if->sk_jfree_listhead); + SLIST_INIT(&sc_if->sk_jinuse_listhead); - m = m_head; - cur = frag = *txidx; + /* create parent tag */ + error = bus_dma_tag_create(NULL, /* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ + 0, /* nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc_if->sk_cdata.sk_parent_tag); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to create parent DMA tag\n"); + goto fail; + } + /* create tag for Tx ring */ + error = bus_dma_tag_create(sc_if->sk_cdata.sk_parent_tag,/* parent */ + 8, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + SK_TX_RING_SZ, /* maxsize */ + 1, /* nsegments */ + SK_TX_RING_SZ, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc_if->sk_cdata.sk_tx_ring_tag); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to allocate Tx ring DMA tag\n"); + goto fail; + } - /* - * Start packing the mbufs in this chain into - * the fragment pointers. Stop when we run out - * of fragments or hit the end of the mbuf chain. - */ - for (m = m_head; m != NULL; m = m->m_next) { - if (m->m_len != 0) { - if ((SK_TX_RING_CNT - - (sc_if->sk_cdata.sk_tx_cnt + cnt)) < 2) - return(ENOBUFS); - f = &sc_if->sk_rdata->sk_tx_ring[frag]; - f->sk_data_lo = vtophys(mtod(m, vm_offset_t)); - f->sk_ctl = m->m_len | SK_OPCODE_DEFAULT; - if (cnt == 0) - f->sk_ctl |= SK_TXCTL_FIRSTFRAG; - else - f->sk_ctl |= SK_TXCTL_OWN; - cur = frag; - SK_INC(frag, SK_TX_RING_CNT); - cnt++; - } + /* create tag for Rx ring */ + error = bus_dma_tag_create(sc_if->sk_cdata.sk_parent_tag,/* parent */ + 8, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + SK_RX_RING_SZ, /* maxsize */ + 1, /* nsegments */ + SK_RX_RING_SZ, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc_if->sk_cdata.sk_rx_ring_tag); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to allocate Rx ring DMA tag\n"); + goto fail; } - if (m != NULL) - return(ENOBUFS); + /* create tag for jumbo Rx ring */ + error = bus_dma_tag_create(sc_if->sk_cdata.sk_parent_tag,/* parent */ + 8, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + SK_JUMBO_RX_RING_SZ, /* maxsize */ + 1, /* nsegments */ + SK_JUMBO_RX_RING_SZ, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc_if->sk_cdata.sk_jumbo_rx_ring_tag); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to allocate jumbo Rx ring DMA tag\n"); + goto fail; + } - sc_if->sk_rdata->sk_tx_ring[cur].sk_ctl |= - SK_TXCTL_LASTFRAG|SK_TXCTL_EOF_INTR; - sc_if->sk_cdata.sk_tx_chain[cur].sk_mbuf = m_head; - sc_if->sk_rdata->sk_tx_ring[*txidx].sk_ctl |= SK_TXCTL_OWN; - sc_if->sk_cdata.sk_tx_cnt += cnt; + /* create tag for jumbo buffer blocks */ + error = bus_dma_tag_create(sc_if->sk_cdata.sk_parent_tag,/* parent */ + PAGE_SIZE, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + SK_JMEM, /* maxsize */ + 1, /* nsegments */ + SK_JMEM, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc_if->sk_cdata.sk_jumbo_tag); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to allocate jumbo Rx buffer block DMA tag\n"); + goto fail; + } - *txidx = frag; + /* create tag for Tx buffers */ + error = bus_dma_tag_create(sc_if->sk_cdata.sk_parent_tag,/* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES * SK_MAXTXSEGS, /* maxsize */ + SK_MAXTXSEGS, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc_if->sk_cdata.sk_tx_tag); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to allocate Tx DMA tag\n"); + goto fail; + } - return(0); -} + /* create tag for Rx buffers */ + error = bus_dma_tag_create(sc_if->sk_cdata.sk_parent_tag,/* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, /* maxsize */ + 1, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc_if->sk_cdata.sk_rx_tag); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to allocate Rx DMA tag\n"); + goto fail; + } + + /* create tag for jumbo Rx buffers */ + error = bus_dma_tag_create(sc_if->sk_cdata.sk_parent_tag,/* parent */ + PAGE_SIZE, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES * SK_MAXRXSEGS, /* maxsize */ + SK_MAXRXSEGS, /* nsegments */ + SK_JLEN, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc_if->sk_cdata.sk_jumbo_rx_tag); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to allocate jumbo Rx DMA tag\n"); + goto fail; + } + + /* allocate DMA'able memory and load the DMA map for Tx ring */ + error = bus_dmamem_alloc(sc_if->sk_cdata.sk_tx_ring_tag, + (void **)&sc_if->sk_rdata.sk_tx_ring, BUS_DMA_NOWAIT | BUS_DMA_ZERO, + &sc_if->sk_cdata.sk_tx_ring_map); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to allocate DMA'able memory for Tx\n"); + goto fail; + } + + ctx.sk_busaddr = 0; + error = bus_dmamap_load(sc_if->sk_cdata.sk_tx_ring_tag, + sc_if->sk_cdata.sk_tx_ring_map, sc_if->sk_rdata.sk_tx_ring, + SK_TX_RING_SZ, sk_dmamap_cb, &ctx, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to load DMA'able memory for Tx ring\n"); + goto fail; + } + sc_if->sk_rdata.sk_tx_ring_paddr = ctx.sk_busaddr; + + /* allocate DMA'able memory and load the DMA map for Rx ring */ + error = bus_dmamem_alloc(sc_if->sk_cdata.sk_rx_ring_tag, + (void **)&sc_if->sk_rdata.sk_rx_ring, BUS_DMA_NOWAIT | BUS_DMA_ZERO, + &sc_if->sk_cdata.sk_rx_ring_map); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to allocate DMA'able memory for Rx ring\n"); + goto fail; + } + + ctx.sk_busaddr = 0; + error = bus_dmamap_load(sc_if->sk_cdata.sk_rx_ring_tag, + sc_if->sk_cdata.sk_rx_ring_map, sc_if->sk_rdata.sk_rx_ring, + SK_RX_RING_SZ, sk_dmamap_cb, &ctx, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to load DMA'able memory for Rx ring\n"); + goto fail; + } + sc_if->sk_rdata.sk_rx_ring_paddr = ctx.sk_busaddr; + + /* allocate DMA'able memory and load the DMA map for jumbo Rx ring */ + error = bus_dmamem_alloc(sc_if->sk_cdata.sk_jumbo_rx_ring_tag, + (void **)&sc_if->sk_rdata.sk_jumbo_rx_ring, + BUS_DMA_NOWAIT|BUS_DMA_ZERO, &sc_if->sk_cdata.sk_jumbo_rx_ring_map); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to allocate DMA'able memory for jumbo Rx ring\n"); + goto fail; + } + + ctx.sk_busaddr = 0; + error = bus_dmamap_load(sc_if->sk_cdata.sk_jumbo_rx_ring_tag, + sc_if->sk_cdata.sk_jumbo_rx_ring_map, + sc_if->sk_rdata.sk_jumbo_rx_ring, SK_JUMBO_RX_RING_SZ, sk_dmamap_cb, + &ctx, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to load DMA'able memory for jumbo Rx ring\n"); + goto fail; + } + sc_if->sk_rdata.sk_jumbo_rx_ring_paddr = ctx.sk_busaddr; + + /* create DMA maps for Tx buffers */ + for (i = 0; i < SK_TX_RING_CNT; i++) { + txd = &sc_if->sk_cdata.sk_txdesc[i]; + txd->tx_m = NULL; + txd->tx_dmamap = 0; + error = bus_dmamap_create(sc_if->sk_cdata.sk_tx_tag, 0, + &txd->tx_dmamap); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to create Tx dmamap\n"); + goto fail; + } + } + /* create DMA maps for Rx buffers */ + if ((error = bus_dmamap_create(sc_if->sk_cdata.sk_rx_tag, 0, + &sc_if->sk_cdata.sk_rx_sparemap)) != 0) { + device_printf(sc_if->sk_if_dev, + "failed to create spare Rx dmamap\n"); + goto fail; + } + for (i = 0; i < SK_RX_RING_CNT; i++) { + rxd = &sc_if->sk_cdata.sk_rxdesc[i]; + rxd->rx_m = NULL; + rxd->rx_dmamap = 0; + error = bus_dmamap_create(sc_if->sk_cdata.sk_rx_tag, 0, + &rxd->rx_dmamap); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to create Rx dmamap\n"); + goto fail; + } + } + /* create DMA maps for jumbo Rx buffers */ + if ((error = bus_dmamap_create(sc_if->sk_cdata.sk_jumbo_rx_tag, 0, + &sc_if->sk_cdata.sk_jumbo_rx_sparemap)) != 0) { + device_printf(sc_if->sk_if_dev, + "failed to create spare jumbo Rx dmamap\n"); + goto fail; + } + for (i = 0; i < SK_JUMBO_RX_RING_CNT; i++) { + jrxd = &sc_if->sk_cdata.sk_jumbo_rxdesc[i]; + jrxd->rx_m = NULL; + jrxd->rx_dmamap = 0; + error = bus_dmamap_create(sc_if->sk_cdata.sk_jumbo_rx_tag, 0, + &jrxd->rx_dmamap); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to create jumbo Rx dmamap\n"); + goto fail; + } + } + + /* allocate DMA'able memory and load the DMA map for jumbo buf */ + error = bus_dmamem_alloc(sc_if->sk_cdata.sk_jumbo_tag, + (void **)&sc_if->sk_rdata.sk_jumbo_buf, + BUS_DMA_NOWAIT|BUS_DMA_ZERO, &sc_if->sk_cdata.sk_jumbo_map); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to allocate DMA'able memory for jumbo buf\n"); + goto fail; + } + + ctx.sk_busaddr = 0; + error = bus_dmamap_load(sc_if->sk_cdata.sk_jumbo_tag, + sc_if->sk_cdata.sk_jumbo_map, + sc_if->sk_rdata.sk_jumbo_buf, SK_JMEM, sk_dmamap_cb, + &ctx, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "failed to load DMA'able memory for jumbobuf\n"); + goto fail; + } + sc_if->sk_rdata.sk_jumbo_buf_paddr = ctx.sk_busaddr; + + /* + * Now divide it up into 9K pieces and save the addresses + * in an array. + */ + ptr = sc_if->sk_rdata.sk_jumbo_buf; + for (i = 0; i < SK_JSLOTS; i++) { + sc_if->sk_cdata.sk_jslots[i] = ptr; + ptr += SK_JLEN; + entry = malloc(sizeof(struct sk_jpool_entry), + M_DEVBUF, M_NOWAIT); + if (entry == NULL) { + device_printf(sc_if->sk_if_dev, + "no memory for jumbo buffer queue!\n"); + error = ENOMEM; + goto fail; + } + entry->slot = i; + SLIST_INSERT_HEAD(&sc_if->sk_jfree_listhead, entry, + jpool_entries); + } + +fail: + return (error); +} + +static void +sk_dma_free(sc_if) + struct sk_if_softc *sc_if; +{ + struct sk_txdesc *txd; + struct sk_rxdesc *rxd; + struct sk_rxdesc *jrxd; + struct sk_jpool_entry *entry; + int i; + + SK_JLIST_LOCK(sc_if); + while ((entry = SLIST_FIRST(&sc_if->sk_jinuse_listhead))) { + device_printf(sc_if->sk_if_dev, + "asked to free buffer that is in use!\n"); + SLIST_REMOVE_HEAD(&sc_if->sk_jinuse_listhead, jpool_entries); + SLIST_INSERT_HEAD(&sc_if->sk_jfree_listhead, entry, + jpool_entries); + } + + while (!SLIST_EMPTY(&sc_if->sk_jfree_listhead)) { + entry = SLIST_FIRST(&sc_if->sk_jfree_listhead); + SLIST_REMOVE_HEAD(&sc_if->sk_jfree_listhead, jpool_entries); + free(entry, M_DEVBUF); + } + SK_JLIST_UNLOCK(sc_if); + + /* destroy jumbo buffer block */ + if (sc_if->sk_cdata.sk_jumbo_map) + bus_dmamap_unload(sc_if->sk_cdata.sk_jumbo_tag, + sc_if->sk_cdata.sk_jumbo_map); + + if (sc_if->sk_rdata.sk_jumbo_buf) { + bus_dmamem_free(sc_if->sk_cdata.sk_jumbo_tag, + sc_if->sk_rdata.sk_jumbo_buf, + sc_if->sk_cdata.sk_jumbo_map); + sc_if->sk_rdata.sk_jumbo_buf = NULL; + sc_if->sk_cdata.sk_jumbo_map = 0; + } + + /* Tx ring */ + if (sc_if->sk_cdata.sk_tx_ring_tag) { + if (sc_if->sk_cdata.sk_tx_ring_map) + bus_dmamap_unload(sc_if->sk_cdata.sk_tx_ring_tag, + sc_if->sk_cdata.sk_tx_ring_map); + if (sc_if->sk_cdata.sk_tx_ring_map && + sc_if->sk_rdata.sk_tx_ring) + bus_dmamem_free(sc_if->sk_cdata.sk_tx_ring_tag, + sc_if->sk_rdata.sk_tx_ring, + sc_if->sk_cdata.sk_tx_ring_map); + sc_if->sk_rdata.sk_tx_ring = NULL; + sc_if->sk_cdata.sk_tx_ring_map = 0; + bus_dma_tag_destroy(sc_if->sk_cdata.sk_tx_ring_tag); + sc_if->sk_cdata.sk_tx_ring_tag = NULL; + } + /* Rx ring */ + if (sc_if->sk_cdata.sk_rx_ring_tag) { + if (sc_if->sk_cdata.sk_rx_ring_map) + bus_dmamap_unload(sc_if->sk_cdata.sk_rx_ring_tag, + sc_if->sk_cdata.sk_rx_ring_map); + if (sc_if->sk_cdata.sk_rx_ring_map && + sc_if->sk_rdata.sk_rx_ring) + bus_dmamem_free(sc_if->sk_cdata.sk_rx_ring_tag, + sc_if->sk_rdata.sk_rx_ring, + sc_if->sk_cdata.sk_rx_ring_map); + sc_if->sk_rdata.sk_rx_ring = NULL; + sc_if->sk_cdata.sk_rx_ring_map = 0; + bus_dma_tag_destroy(sc_if->sk_cdata.sk_rx_ring_tag); + sc_if->sk_cdata.sk_rx_ring_tag = NULL; + } + /* jumbo Rx ring */ + if (sc_if->sk_cdata.sk_jumbo_rx_ring_tag) { + if (sc_if->sk_cdata.sk_jumbo_rx_ring_map) + bus_dmamap_unload(sc_if->sk_cdata.sk_jumbo_rx_ring_tag, + sc_if->sk_cdata.sk_jumbo_rx_ring_map); + if (sc_if->sk_cdata.sk_jumbo_rx_ring_map && + sc_if->sk_rdata.sk_jumbo_rx_ring) + bus_dmamem_free(sc_if->sk_cdata.sk_jumbo_rx_ring_tag, + sc_if->sk_rdata.sk_jumbo_rx_ring, + sc_if->sk_cdata.sk_jumbo_rx_ring_map); + sc_if->sk_rdata.sk_jumbo_rx_ring = NULL; + sc_if->sk_cdata.sk_jumbo_rx_ring_map = 0; + bus_dma_tag_destroy(sc_if->sk_cdata.sk_jumbo_rx_ring_tag); + sc_if->sk_cdata.sk_jumbo_rx_ring_tag = NULL; + } + /* Tx buffers */ + if (sc_if->sk_cdata.sk_tx_tag) { + for (i = 0; i < SK_TX_RING_CNT; i++) { + txd = &sc_if->sk_cdata.sk_txdesc[i]; + if (txd->tx_dmamap) { + bus_dmamap_destroy(sc_if->sk_cdata.sk_tx_tag, + txd->tx_dmamap); + txd->tx_dmamap = 0; + } + } + bus_dma_tag_destroy(sc_if->sk_cdata.sk_tx_tag); + sc_if->sk_cdata.sk_tx_tag = NULL; + } + /* Rx buffers */ + if (sc_if->sk_cdata.sk_rx_tag) { + for (i = 0; i < SK_RX_RING_CNT; i++) { + rxd = &sc_if->sk_cdata.sk_rxdesc[i]; + if (rxd->rx_dmamap) { + bus_dmamap_destroy(sc_if->sk_cdata.sk_rx_tag, + rxd->rx_dmamap); + rxd->rx_dmamap = 0; + } + } + if (sc_if->sk_cdata.sk_rx_sparemap) { + bus_dmamap_destroy(sc_if->sk_cdata.sk_rx_tag, + sc_if->sk_cdata.sk_rx_sparemap); + sc_if->sk_cdata.sk_rx_sparemap = 0; + } + bus_dma_tag_destroy(sc_if->sk_cdata.sk_rx_tag); + sc_if->sk_cdata.sk_rx_tag = NULL; + } + /* jumbo Rx buffers */ + if (sc_if->sk_cdata.sk_jumbo_rx_tag) { + for (i = 0; i < SK_JUMBO_RX_RING_CNT; i++) { + jrxd = &sc_if->sk_cdata.sk_jumbo_rxdesc[i]; + if (jrxd->rx_dmamap) { + bus_dmamap_destroy( + sc_if->sk_cdata.sk_jumbo_rx_tag, + jrxd->rx_dmamap); + jrxd->rx_dmamap = 0; + } + } + if (sc_if->sk_cdata.sk_jumbo_rx_sparemap) { + bus_dmamap_destroy(sc_if->sk_cdata.sk_jumbo_rx_tag, + sc_if->sk_cdata.sk_jumbo_rx_sparemap); + sc_if->sk_cdata.sk_jumbo_rx_sparemap = 0; + } + bus_dma_tag_destroy(sc_if->sk_cdata.sk_jumbo_rx_tag); + sc_if->sk_cdata.sk_jumbo_rx_tag = NULL; + } + + if (sc_if->sk_cdata.sk_parent_tag) { + bus_dma_tag_destroy(sc_if->sk_cdata.sk_parent_tag); + sc_if->sk_cdata.sk_parent_tag = NULL; + } + mtx_destroy(&sc_if->sk_jlist_mtx); +} + +/* + * Allocate a jumbo buffer. + */ +static void * +sk_jalloc(sc_if) + struct sk_if_softc *sc_if; +{ + struct sk_jpool_entry *entry; + + SK_JLIST_LOCK(sc_if); + + entry = SLIST_FIRST(&sc_if->sk_jfree_listhead); + + if (entry == NULL) { + SK_JLIST_UNLOCK(sc_if); + return (NULL); + } + + SLIST_REMOVE_HEAD(&sc_if->sk_jfree_listhead, jpool_entries); + SLIST_INSERT_HEAD(&sc_if->sk_jinuse_listhead, entry, jpool_entries); + + SK_JLIST_UNLOCK(sc_if); + + return (sc_if->sk_cdata.sk_jslots[entry->slot]); +} + +/* + * Release a jumbo buffer. + */ +static void +sk_jfree(buf, args) + void *buf; + void *args; +{ + struct sk_if_softc *sc_if; + struct sk_jpool_entry *entry; + int i; + + /* Extract the softc struct pointer. */ + sc_if = (struct sk_if_softc *)args; + KASSERT(sc_if != NULL, ("%s: can't find softc pointer!", __func__)); + + SK_JLIST_LOCK(sc_if); + /* calculate the slot this buffer belongs to */ + i = ((vm_offset_t)buf + - (vm_offset_t)sc_if->sk_rdata.sk_jumbo_buf) / SK_JLEN; + KASSERT(i >= 0 && i < SK_JSLOTS, + ("%s: asked to free buffer that we don't manage!", __func__)); + + entry = SLIST_FIRST(&sc_if->sk_jinuse_listhead); + KASSERT(entry != NULL, ("%s: buffer not in use!", __func__)); + entry->slot = i; + SLIST_REMOVE_HEAD(&sc_if->sk_jinuse_listhead, jpool_entries); + SLIST_INSERT_HEAD(&sc_if->sk_jfree_listhead, entry, jpool_entries); + if (SLIST_EMPTY(&sc_if->sk_jinuse_listhead)) + wakeup(sc_if); + + SK_JLIST_UNLOCK(sc_if); +} + +static void +sk_txcksum(ifp, m, f) + struct ifnet *ifp; + struct mbuf *m; + struct sk_tx_desc *f; +{ + struct ip *ip; + u_int16_t offset; + caddr_t p; + + offset = sizeof(struct ip) + ETHER_HDR_LEN; + for(; m && m->m_len == 0; m = m->m_next) + ; + if (m == NULL || m->m_len < ETHER_HDR_LEN) { + if_printf(ifp, "%s: m_len < ETHER_HDR_LEN\n", __func__); + /* checksum may be corrupted */ + goto sendit; + } + if (m->m_len < ETHER_HDR_LEN + sizeof(u_int32_t)) { + if (m->m_len != ETHER_HDR_LEN) { + if_printf(ifp, "%s: m_len != ETHER_HDR_LEN\n", + __func__); + /* checksum may be corrupted */ + goto sendit; + } + for(m = m->m_next; m && m->m_len == 0; m = m->m_next) + ; + if (m == NULL) { + offset = sizeof(struct ip) + ETHER_HDR_LEN; + /* checksum may be corrupted */ + goto sendit; + } + ip = mtod(m, struct ip *); + } else { + p = mtod(m, caddr_t); + p += ETHER_HDR_LEN; + ip = (struct ip *)p; + } + offset = (ip->ip_hl << 2) + ETHER_HDR_LEN; + +sendit: + f->sk_csum_startval = 0; + f->sk_csum_start = htole32(((offset + m->m_pkthdr.csum_data) & 0xffff) | + (offset << 16)); +} + +static int +sk_encap(sc_if, m_head) + struct sk_if_softc *sc_if; + struct mbuf **m_head; +{ + struct sk_txdesc *txd; + struct sk_tx_desc *f = NULL; + struct mbuf *m, *n; + bus_dma_segment_t txsegs[SK_MAXTXSEGS]; + u_int32_t cflags, frag, si, sk_ctl; + int error, i, nseg; + + SK_IF_LOCK_ASSERT(sc_if); + + if ((txd = STAILQ_FIRST(&sc_if->sk_cdata.sk_txfreeq)) == NULL) + return (ENOBUFS); + + m = *m_head; + error = bus_dmamap_load_mbuf_sg(sc_if->sk_cdata.sk_tx_tag, + txd->tx_dmamap, m, txsegs, &nseg, 0); + if (error == EFBIG) { + n = m_defrag(m, M_DONTWAIT); + if (n == NULL) { + m_freem(m); + m = NULL; + return (ENOMEM); + } + m = n; + error = bus_dmamap_load_mbuf_sg(sc_if->sk_cdata.sk_tx_tag, + txd->tx_dmamap, m, txsegs, &nseg, 0); + if (error != 0) { + m_freem(m); + m = NULL; + return (error); + } + } else if (error != 0) + return (error); + if (nseg == 0) { + m_freem(m); + m = NULL; + return (EIO); + } + if (sc_if->sk_cdata.sk_tx_cnt + nseg >= SK_TX_RING_CNT) { + bus_dmamap_unload(sc_if->sk_cdata.sk_tx_tag, txd->tx_dmamap); + return (ENOBUFS); + } + + if ((m->m_pkthdr.csum_flags & sc_if->sk_ifp->if_hwassist) != 0) + cflags = SK_OPCODE_CSUM; + else + cflags = SK_OPCODE_DEFAULT; + si = frag = sc_if->sk_cdata.sk_tx_prod; + for (i = 0; i < nseg; i++) { + f = &sc_if->sk_rdata.sk_tx_ring[frag]; + f->sk_data_lo = htole32(SK_ADDR_LO(txsegs[i].ds_addr)); + f->sk_data_hi = htole32(SK_ADDR_HI(txsegs[i].ds_addr)); + sk_ctl = txsegs[i].ds_len | cflags; + if (i == 0) { + if (cflags == SK_OPCODE_CSUM) + sk_txcksum(sc_if->sk_ifp, m, f); + sk_ctl |= SK_TXCTL_FIRSTFRAG; + } else + sk_ctl |= SK_TXCTL_OWN; + f->sk_ctl = htole32(sk_ctl); + sc_if->sk_cdata.sk_tx_cnt++; + SK_INC(frag, SK_TX_RING_CNT); + } + sc_if->sk_cdata.sk_tx_prod = frag; + + /* set EOF on the last desciptor */ + frag = (frag + SK_TX_RING_CNT - 1) % SK_TX_RING_CNT; + f = &sc_if->sk_rdata.sk_tx_ring[frag]; + f->sk_ctl |= htole32(SK_TXCTL_LASTFRAG | SK_TXCTL_EOF_INTR); + + /* turn the first descriptor ownership to NIC */ + f = &sc_if->sk_rdata.sk_tx_ring[si]; + f->sk_ctl |= htole32(SK_TXCTL_OWN); + + STAILQ_REMOVE_HEAD(&sc_if->sk_cdata.sk_txfreeq, tx_q); + STAILQ_INSERT_TAIL(&sc_if->sk_cdata.sk_txbusyq, txd, tx_q); + txd->tx_m = m; + + /* sync descriptors */ + bus_dmamap_sync(sc_if->sk_cdata.sk_tx_tag, txd->tx_dmamap, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc_if->sk_cdata.sk_tx_ring_tag, + sc_if->sk_cdata.sk_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return (0); +} static void sk_start(ifp) @@ -2073,17 +2705,16 @@ { struct sk_softc *sc; struct sk_if_softc *sc_if; - struct mbuf *m_head = NULL; - u_int32_t idx; + struct mbuf *m_head; + int enq; sc_if = ifp->if_softc; sc = sc_if->sk_softc; SK_IF_LOCK_ASSERT(sc_if); - idx = sc_if->sk_cdata.sk_tx_prod; - - while(sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf == NULL) { + for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && + sc_if->sk_cdata.sk_tx_cnt < SK_TX_RING_CNT - 1; ) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; @@ -2093,12 +2724,15 @@ * don't have room, set the OACTIVE flag and wait * for the NIC to drain the ring. */ - if (sk_encap(sc_if, m_head, &idx)) { + if (sk_encap(sc_if, &m_head)) { + if (m_head == NULL) + break; IFQ_DRV_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } + enq++; /* * If there's a BPF listener, bounce a copy of this frame * to him. @@ -2106,16 +2740,13 @@ BPF_MTAP(ifp, m_head); } - /* Transmit */ - if (idx != sc_if->sk_cdata.sk_tx_prod) { - sc_if->sk_cdata.sk_tx_prod = idx; + if (enq > 0) { + /* Transmit */ CSR_WRITE_4(sc, sc_if->sk_tx_bmu, SK_TXBMU_TX_START); /* Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; } - - return; } @@ -2127,8 +2758,8 @@ sc_if = ifp->if_softc; - printf("sk%d: watchdog timeout\n", sc_if->sk_unit); SK_IF_LOCK(sc_if); + if_printf(sc_if->sk_ifp, "watchdog timeout\n"); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; sk_init_locked(sc_if); SK_IF_UNLOCK(sc_if); @@ -2158,6 +2789,111 @@ return; } +/* + * XXX + * It seems that assembler implementation of in_addword() on sparc64 is broken. + * I couldn't get correct checksum calculation from the function so I added + * local hack. This should be removed when we have a correct in_addword() + * implementation. + */ +#if 1 +static __inline u_int16_t +sk_in_addword(a, b) + u_int32_t a; + u_int32_t b; +{ + u_int32_t sum; + + sum = a + b; + if (sum > 0xffff) + sum -= 0xffff; + return (sum); +} +#else +static __inline u_short +sk_in_addword(u_short sum, u_short b) +{ + u_long __ret, __tmp; + + __asm( + "sll %2, 16, %0\n" + "sll %3, 16, %1\n" + "addcc %0, %1, %0\n" + "srl %0, 16, %0\n" + "addc %0, 0, %0\n" + : "=&r" (__ret), "=&r" (__tmp) : "r" (sum), "r" (b)); + return (__ret); +} +#endif + +/* + * According to the data sheet from SK-NET GENESIS the hardware can compute two + * Rx checksums at the same time(Each checksum start position is programmed in + * Rx descriptors). However it seems that TCP/UDP checksum does not work at + * least on my Yukon hardware. I tried every possible combination including FCS + * but couldn't mange to get correct one. So TCP/UDP checksum offload was + * disabled at the moment and only IP checksum offload was enabled. As nomral + * IP header size is 20 bytes I can't expect it would give a increase in + * throughput. However it seems it doesn't hurt performance in my testing. + * If there is a more detailed information for checksum secret of the hardware + * in question please contact yongari@FreeBSD.org to add TCP/UDP checksum + * offload support. + */ +static void +sk_rxcksum(ifp, m, csum) + struct ifnet *ifp; + struct mbuf *m; + u_int32_t csum; +{ + struct ether_header *eh; + struct ip *ip; + int32_t hlen, len, pktlen; + u_int16_t csum1, csum2, ipcsum; + + pktlen = m->m_pkthdr.len; + if (pktlen < sizeof(struct ether_header) + sizeof(struct ip)) + 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; + + csum1 = htons(csum & 0xffff); + csum2 = htons((csum >> 16) & 0xffff); + ipcsum = sk_in_addword(csum1, ~csum2 & 0xffff); + /* checksum fixup for IP options */ + len = hlen - sizeof(struct ip); + if (len > 0) { + /* + * There should be a way to get correct checksums for + * IP datagrams with option headers. + */ + return; + } + if (ipcsum != 0xffff) { + /* + * XXX + * OpenBSD sk(4) says that sk v1 chips can sometimes compute an + * incorrect cksum. So don't mark IP checksum was validated by + * hardware when hardware computed checksum indicates bad + * checksum. + */ + /* m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; */ + return; + } + m->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID; +} + static void sk_rxeof(sc_if) struct sk_if_softc *sc_if; @@ -2165,67 +2901,130 @@ struct sk_softc *sc; struct mbuf *m; struct ifnet *ifp; - struct sk_chain *cur_rx; - int total_len = 0; - int i; - u_int32_t rxstat; + struct sk_rx_desc *cur_rx; + struct sk_rxdesc *rxd; + int idx, prog; + u_int32_t csum, rxstat, sk_ctl; sc = sc_if->sk_softc; ifp = sc_if->sk_ifp; - i = sc_if->sk_cdata.sk_rx_prod; - cur_rx = &sc_if->sk_cdata.sk_rx_chain[i]; SK_LOCK_ASSERT(sc); - while(!(sc_if->sk_rdata->sk_rx_ring[i].sk_ctl & SK_RXCTL_OWN)) { + bus_dmamap_sync(sc_if->sk_cdata.sk_rx_ring_tag, + sc_if->sk_cdata.sk_rx_ring_map, BUS_DMASYNC_POSTREAD); + prog = 0; + for (idx = sc_if->sk_cdata.sk_rx_prod;; SK_INC(idx, SK_RX_RING_CNT)) { + cur_rx = &sc_if->sk_rdata.sk_rx_ring[idx]; + sk_ctl = le32toh(cur_rx->sk_ctl); + if ((sk_ctl & SK_RXCTL_OWN) != 0) + break; + prog++; + rxd = &sc_if->sk_cdata.sk_rxdesc[idx]; + rxstat = le32toh(cur_rx->sk_xmac_rxstat); + if ((rxstat & XM_RXSTAT_ERRFRAME) != 0 || + SK_RXBYTES(sk_ctl) < SK_MIN_FRAMELEN || + SK_RXBYTES(sk_ctl) > SK_MAX_FRAMELEN) { + /* + * XXX + * As XMAC operates in streaming mode, driver can get + * runt packets. This should be removed. + */ + ifp->if_ierrors++; + sk_discard_rxbuf(sc_if, idx); + continue; + } + m = rxd->rx_m; + csum = le32toh(cur_rx->sk_csum); + if (sk_newbuf(sc_if, idx) != 0) { + ifp->if_iqdrops++; + /* reuse old buffer */ + sk_discard_rxbuf(sc_if, idx); + continue; + } + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = SK_RXBYTES(sk_ctl); + ifp->if_ipackets++; + if (ifp->if_capenable & IFCAP_RXCSUM) + sk_rxcksum(ifp, m, csum); + SK_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + SK_LOCK(sc); + } - cur_rx = &sc_if->sk_cdata.sk_rx_chain[i]; - rxstat = sc_if->sk_rdata->sk_rx_ring[i].sk_xmac_rxstat; - m = cur_rx->sk_mbuf; - cur_rx->sk_mbuf = NULL; - total_len = SK_RXBYTES(sc_if->sk_rdata->sk_rx_ring[i].sk_ctl); - SK_INC(i, SK_RX_RING_CNT); + if (prog > 0) { + bus_dmamap_sync(sc_if->sk_cdata.sk_rx_ring_tag, + sc_if->sk_cdata.sk_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + sc_if->sk_cdata.sk_rx_prod = idx; + } +} - if (rxstat & XM_RXSTAT_ERRFRAME) { +static void +sk_jumbo_rxeof(sc_if) + struct sk_if_softc *sc_if; +{ + struct sk_softc *sc; + struct mbuf *m; + struct ifnet *ifp; + struct sk_rx_desc *cur_rx; + struct sk_rxdesc *jrxd; + u_int32_t csum, rxstat, sk_ctl; + int idx, prog; + + sc = sc_if->sk_softc; + ifp = sc_if->sk_ifp; + + SK_LOCK_ASSERT(sc); + + bus_dmamap_sync(sc_if->sk_cdata.sk_jumbo_rx_ring_tag, + sc_if->sk_cdata.sk_jumbo_rx_ring_map, BUS_DMASYNC_POSTREAD); + prog = 0; + for (idx = sc_if->sk_cdata.sk_jumbo_rx_prod;; + SK_INC(idx, SK_RX_RING_CNT)) { + cur_rx = &sc_if->sk_rdata.sk_jumbo_rx_ring[idx]; + sk_ctl = le32toh(cur_rx->sk_ctl); + if ((sk_ctl & SK_RXCTL_OWN) != 0) + break; + prog++; + jrxd = &sc_if->sk_cdata.sk_jumbo_rxdesc[idx]; + rxstat = le32toh(cur_rx->sk_xmac_rxstat); + if ((rxstat & XM_RXSTAT_ERRFRAME) != 0 || + SK_RXBYTES(sk_ctl) < SK_MIN_FRAMELEN || + SK_RXBYTES(sk_ctl) > SK_JUMBO_FRAMELEN) { + /* + * XXX + * As XMAC operates in streaming mode, driver can get + * runt packets. + */ ifp->if_ierrors++; - sk_newbuf(sc_if, cur_rx, m); + sk_discard_jumbo_rxbuf(sc_if, idx); continue; } - - /* - * Try to allocate a new jumbo buffer. If that - * fails, copy the packet to mbufs and put the - * jumbo buffer back in the ring so it can be - * re-used. If allocating mbufs fails, then we - * have to drop the packet. - */ - if (sk_newbuf(sc_if, cur_rx, NULL) == ENOBUFS) { - struct mbuf *m0; - m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, - ifp, NULL); - sk_newbuf(sc_if, cur_rx, m); - if (m0 == NULL) { - printf("sk%d: no receive buffers " - "available -- packet dropped!\n", - sc_if->sk_unit); - ifp->if_ierrors++; - continue; - } - m = m0; - } else { - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = m->m_len = total_len; + m = jrxd->rx_m; + csum = le32toh(cur_rx->sk_csum); + if (sk_jumbo_newbuf(sc_if, idx) != 0) { + ifp->if_iqdrops++; + /* reuse old buffer */ + sk_discard_jumbo_rxbuf(sc_if, idx); + continue; } - + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = SK_RXBYTES(sk_ctl); ifp->if_ipackets++; + if (ifp->if_capenable & IFCAP_RXCSUM) + sk_rxcksum(ifp, m, csum); SK_UNLOCK(sc); (*ifp->if_input)(ifp, m); SK_LOCK(sc); } - sc_if->sk_cdata.sk_rx_prod = i; - - return; + if (prog > 0) { + bus_dmamap_sync(sc_if->sk_cdata.sk_jumbo_rx_ring_tag, + sc_if->sk_cdata.sk_jumbo_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + sc_if->sk_cdata.sk_jumbo_rx_prod = idx; + } } static void @@ -2233,41 +3032,61 @@ struct sk_if_softc *sc_if; { struct sk_softc *sc; + struct sk_txdesc *txd; struct sk_tx_desc *cur_tx; struct ifnet *ifp; - u_int32_t idx; + u_int32_t idx, sk_ctl; sc = sc_if->sk_softc; ifp = sc_if->sk_ifp; + txd = STAILQ_FIRST(&sc_if->sk_cdata.sk_txbusyq); + if (txd == NULL) + return; + bus_dmamap_sync(sc_if->sk_cdata.sk_tx_ring_tag, + sc_if->sk_cdata.sk_tx_ring_map, BUS_DMASYNC_POSTREAD); /* * Go through our tx ring and free mbufs for those * frames that have been sent. */ - idx = sc_if->sk_cdata.sk_tx_cons; - while(idx != sc_if->sk_cdata.sk_tx_prod) { - cur_tx = &sc_if->sk_rdata->sk_tx_ring[idx]; - if (cur_tx->sk_ctl & SK_TXCTL_OWN) - break; - if (cur_tx->sk_ctl & SK_TXCTL_LASTFRAG) - ifp->if_opackets++; - if (sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf != NULL) { - m_freem(sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf); - sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf = NULL; - } + for (idx = sc_if->sk_cdata.sk_tx_cons;; SK_INC(idx, SK_TX_RING_CNT)) { + if (sc_if->sk_cdata.sk_tx_cnt <= 0) + break; + cur_tx = &sc_if->sk_rdata.sk_tx_ring[idx]; + sk_ctl = le32toh(cur_tx->sk_ctl); + if (sk_ctl & SK_TXCTL_OWN) + break; sc_if->sk_cdata.sk_tx_cnt--; - SK_INC(idx, SK_TX_RING_CNT); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + if ((sk_ctl & SK_TXCTL_LASTFRAG) == 0) + continue; + bus_dmamap_sync(sc_if->sk_cdata.sk_tx_tag, txd->tx_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc_if->sk_cdata.sk_tx_tag, txd->tx_dmamap); + + ifp->if_opackets++; + m_freem(txd->tx_m); + txd->tx_m = NULL; + STAILQ_REMOVE_HEAD(&sc_if->sk_cdata.sk_txbusyq, tx_q); + STAILQ_INSERT_TAIL(&sc_if->sk_cdata.sk_txfreeq, txd, tx_q); + txd = STAILQ_FIRST(&sc_if->sk_cdata.sk_txbusyq); } + sc_if->sk_cdata.sk_tx_cons = idx; + ifp->if_timer = sc_if->sk_cdata.sk_tx_cnt > 0 ? 5 : 0; - if (sc_if->sk_cdata.sk_tx_cnt == 0) { - ifp->if_timer = 0; - } else /* nudge chip to keep tx ring moving */ + bus_dmamap_sync(sc_if->sk_cdata.sk_tx_ring_tag, + sc_if->sk_cdata.sk_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + /* + * SK-NET GENESIS data sheet says that possibility of losing Start + * transmit command due to CPU/cache related interim storage problems + * under certain conditions. The document recommends a polling + * mechanism to send a Start transmit command to initiate transfer + * of ready descriptors regulary. We wake up hardware at the end of + * Tx path when there are pending descriptors to be transmmited. + */ + if (sc_if->sk_cdata.sk_tx_cnt > 0) CSR_WRITE_4(sc, sc_if->sk_tx_bmu, SK_TXBMU_TX_START); - - if (sc_if->sk_cdata.sk_tx_cnt < SK_TX_RING_CNT - 2) - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - - sc_if->sk_cdata.sk_tx_cons = idx; } static void @@ -2280,18 +3099,14 @@ int i; sc_if = xsc_if; - SK_IF_LOCK(sc_if); ifp = sc_if->sk_ifp; mii = device_get_softc(sc_if->sk_miibus); - if (!(ifp->if_flags & IFF_UP)) { - SK_IF_UNLOCK(sc_if); + if (!(ifp->if_flags & IFF_UP)) return; - } if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) { sk_intr_bcom(sc_if); - SK_IF_UNLOCK(sc_if); return; } @@ -2308,8 +3123,7 @@ } if (i != 3) { - sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); - SK_IF_UNLOCK(sc_if); + callout_reset(&sc_if->sk_tick_ch, hz, sk_tick, sc_if); return; } @@ -2317,10 +3131,7 @@ SK_XM_CLRBIT_2(sc_if, XM_IMR, XM_IMR_GP0_SET); SK_XM_READ_2(sc_if, XM_ISR); mii_tick(mii); - untimeout(sk_tick, sc_if, sc_if->sk_tick_ch); - - SK_IF_UNLOCK(sc_if); - return; + callout_stop(&sc_if->sk_tick_ch); } static void @@ -2368,7 +3179,7 @@ SK_LINKLED_BLINK_OFF); } else { mii_tick(mii); - sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); + callout_reset(&sc_if->sk_tick_ch, hz, sk_tick, sc_if); } } @@ -2394,11 +3205,11 @@ if (sc_if->sk_phytype == SK_PHYTYPE_XMAC) { if (status & XM_ISR_GP0_SET) { SK_XM_SETBIT_2(sc_if, XM_IMR, XM_IMR_GP0_SET); - sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); + callout_reset(&sc_if->sk_tick_ch, hz, sk_tick, sc_if); } if (status & XM_ISR_AUTONEG_DONE) { - sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); + callout_reset(&sc_if->sk_tick_ch, hz, sk_tick, sc_if); } } @@ -2429,7 +3240,7 @@ void *xsc; { struct sk_softc *sc = xsc; - struct sk_if_softc *sc_if0 = NULL, *sc_if1 = NULL; + struct sk_if_softc *sc_if0, *sc_if1; struct ifnet *ifp0 = NULL, *ifp1 = NULL; u_int32_t status; @@ -2450,12 +3261,18 @@ /* Handle receive interrupts first. */ if (status & SK_ISR_RX1_EOF) { - sk_rxeof(sc_if0); + if (ifp0->if_mtu > SK_MAX_FRAMELEN) + sk_jumbo_rxeof(sc_if0); + else + sk_rxeof(sc_if0); CSR_WRITE_4(sc, SK_BMU_RX_CSR0, SK_RXBMU_CLR_IRQ_EOF|SK_RXBMU_RX_START); } if (status & SK_ISR_RX2_EOF) { - sk_rxeof(sc_if1); + if (ifp1->if_mtu > SK_MAX_FRAMELEN) + sk_jumbo_rxeof(sc_if1); + else + sk_rxeof(sc_if1); CSR_WRITE_4(sc, SK_BMU_RX_CSR1, SK_RXBMU_CLR_IRQ_EOF|SK_RXBMU_RX_START); } @@ -2523,6 +3340,8 @@ { 0x15, 0x0232 }, { 0x17, 0x800D }, { 0x15, 0x000F }, { 0x18, 0x0420 }, { 0, 0 } }; + SK_IF_LOCK_ASSERT(sc_if); + sc = sc_if->sk_softc; ifp = sc_if->sk_ifp; @@ -2613,13 +3432,12 @@ * case the XMAC will start transfering frames out of the * RX FIFO as soon as the FIFO threshold is reached. */ - SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_BADFRAMES| - XM_MODE_RX_GIANTS|XM_MODE_RX_RUNTS|XM_MODE_RX_CRCERRS| - XM_MODE_RX_INRANGELEN); - - if (ifp->if_mtu > (ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN)) + if (ifp->if_mtu > SK_MAX_FRAMELEN) { + SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_BADFRAMES| + XM_MODE_RX_GIANTS|XM_MODE_RX_RUNTS|XM_MODE_RX_CRCERRS| + XM_MODE_RX_INRANGELEN); SK_XM_SETBIT_2(sc_if, XM_RXCMD, XM_RXCMD_BIGPKTOK); - else + } else SK_XM_CLRBIT_2(sc_if, XM_RXCMD, XM_RXCMD_BIGPKTOK); /* @@ -2686,6 +3504,8 @@ struct ifnet *ifp; int i; + SK_IF_LOCK_ASSERT(sc_if); + sc = sc_if->sk_softc; ifp = sc_if->sk_ifp; @@ -2747,14 +3567,14 @@ /* serial mode register */ reg = YU_SMR_DATA_BLIND(0x1c) | YU_SMR_MFL_VLAN | YU_SMR_IPG_DATA(0x1e); - if (ifp->if_mtu > (ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN)) + if (ifp->if_mtu > SK_MAX_FRAMELEN) reg |= YU_SMR_MFL_JUMBO; SK_YU_WRITE_2(sc_if, YUKON_SMR, reg); /* Setup Yukon's address */ for (i = 0; i < 3; i++) { /* Write Source Address 1 (unicast filter) */ - SK_YU_WRITE_2(sc_if, YUKON_SAL1 + i * 4, + SK_YU_WRITE_2(sc_if, YUKON_SAL1 + i * 4, IF_LLADDR(sc_if->sk_ifp)[i * 2] | IF_LLADDR(sc_if->sk_ifp)[i * 2 + 1] << 8); } @@ -2811,6 +3631,7 @@ struct mii_data *mii; u_int16_t reg; u_int32_t imr; + int error; SK_IF_LOCK_ASSERT(sc_if); @@ -2831,7 +3652,7 @@ SK_LINKLED_LINKSYNC_ON); /* Configure RX LED */ - SK_IF_WRITE_1(sc_if, 0, SK_RXLED1_CTL, + SK_IF_WRITE_1(sc_if, 0, SK_RXLED1_CTL, SK_RXLEDCTL_COUNTER_START); /* Configure TX LED */ @@ -2887,34 +3708,47 @@ /* Configure BMUs */ SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_BMU_CSR, SK_RXBMU_ONLINE); - SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_LO, - vtophys(&sc_if->sk_rdata->sk_rx_ring[0])); - SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_HI, 0); + if (ifp->if_mtu > SK_MAX_FRAMELEN) { + SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_LO, + SK_ADDR_LO(SK_JUMBO_RX_RING_ADDR(sc_if, 0))); + SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_HI, + SK_ADDR_HI(SK_JUMBO_RX_RING_ADDR(sc_if, 0))); + } else { + SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_LO, + SK_ADDR_LO(SK_RX_RING_ADDR(sc_if, 0))); + SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_HI, + SK_ADDR_HI(SK_RX_RING_ADDR(sc_if, 0))); + } SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_BMU_CSR, SK_TXBMU_ONLINE); SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_CURADDR_LO, - vtophys(&sc_if->sk_rdata->sk_tx_ring[0])); - SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_CURADDR_HI, 0); + SK_ADDR_LO(SK_TX_RING_ADDR(sc_if, 0))); + SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_CURADDR_HI, + SK_ADDR_HI(SK_TX_RING_ADDR(sc_if, 0))); /* Init descriptors */ - if (sk_init_rx_ring(sc_if) == ENOBUFS) { - printf("sk%d: initialization failed: no " - "memory for rx buffers\n", sc_if->sk_unit); + if (ifp->if_mtu > SK_MAX_FRAMELEN) + error = sk_init_jumbo_rx_ring(sc_if); + else + error = sk_init_rx_ring(sc_if); + if (error != 0) { + device_printf(sc_if->sk_if_dev, + "initialization failed: no memory for rx buffers\n"); sk_stop(sc_if); return; } sk_init_tx_ring(sc_if); /* Set interrupt moderation if changed via sysctl. */ - /* SK_LOCK(sc); */ imr = sk_win_read_4(sc, SK_IMTIMERINIT); - if (imr != SK_IM_USECS(sc->sk_int_mod)) { - sk_win_write_4(sc, SK_IMTIMERINIT, SK_IM_USECS(sc->sk_int_mod)); + if (imr != SK_IM_USECS(sc->sk_int_mod, sc->sk_int_ticks)) { + sk_win_write_4(sc, SK_IMTIMERINIT, SK_IM_USECS(sc->sk_int_mod, + sc->sk_int_ticks)); if (bootverbose) - printf("skc%d: interrupt moderation is %d us\n", - sc->sk_unit, sc->sk_int_mod); + device_printf(sc_if->sk_if_dev, + "interrupt moderation is %d us.\n", + sc->sk_int_mod); } - /* SK_UNLOCK(sc); */ /* Configure interrupt handling */ CSR_READ_4(sc, SK_ISSR); @@ -2957,13 +3791,16 @@ { int i; struct sk_softc *sc; + struct sk_txdesc *txd; + struct sk_rxdesc *rxd; + struct sk_rxdesc *jrxd; struct ifnet *ifp; SK_IF_LOCK_ASSERT(sc_if); sc = sc_if->sk_softc; ifp = sc_if->sk_ifp; - untimeout(sk_tick, sc_if, sc_if->sk_tick_ch); + callout_stop(&sc_if->sk_tick_ch); if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) { u_int32_t val; @@ -3016,16 +3853,36 @@ /* Free RX and TX mbufs still in the queues. */ for (i = 0; i < SK_RX_RING_CNT; i++) { - if (sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf != NULL) { - m_freem(sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf); - sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf = NULL; + rxd = &sc_if->sk_cdata.sk_rxdesc[i]; + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc_if->sk_cdata.sk_rx_tag, + rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc_if->sk_cdata.sk_rx_tag, + rxd->rx_dmamap); + m_freem(rxd->rx_m); + rxd->rx_m = NULL; + } + } + for (i = 0; i < SK_JUMBO_RX_RING_CNT; i++) { + jrxd = &sc_if->sk_cdata.sk_jumbo_rxdesc[i]; + if (jrxd->rx_m != NULL) { + bus_dmamap_sync(sc_if->sk_cdata.sk_jumbo_rx_tag, + jrxd->rx_dmamap, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc_if->sk_cdata.sk_jumbo_rx_tag, + jrxd->rx_dmamap); + m_freem(jrxd->rx_m); + jrxd->rx_m = NULL; } } - for (i = 0; i < SK_TX_RING_CNT; i++) { - if (sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf != NULL) { - m_freem(sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf); - sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf = NULL; + txd = &sc_if->sk_cdata.sk_txdesc[i]; + if (txd->tx_m != NULL) { + bus_dmamap_sync(sc_if->sk_cdata.sk_tx_tag, + txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc_if->sk_cdata.sk_tx_tag, + txd->tx_dmamap); + m_freem(txd->tx_m); + txd->tx_m = NULL; } } --- if_skreg.h.orig Tue Oct 18 13:19:10 2005 +++ if_skreg.h Thu Jan 12 18:55:53 2006 @@ -378,8 +378,10 @@ #define SK_IMCTL_STOP 0x02 #define SK_IMCTL_START 0x04 -#define SK_IMTIMER_TICKS 54 -#define SK_IM_USECS(x) ((x) * SK_IMTIMER_TICKS) +#define SK_IMTIMER_TICKS_GENESIS 53 +#define SK_IMTIMER_TICKS_YUKON 78 +#define SK_IMTIMER_TICKS_YUKON_EC 125 +#define SK_IM_USECS(x, t) ((x) * (t)) #define SK_IM_MIN 10 #define SK_IM_DEFAULT 100 @@ -1308,6 +1310,9 @@ char *sk_name; }; +#define SK_ADDR_LO(x) ((u_int64_t) (x) & 0xffffffff) +#define SK_ADDR_HI(x) ((u_int64_t) (x) >> 32) + /* RX queue descriptor data structure */ struct sk_rx_desc { u_int32_t sk_ctl; @@ -1316,10 +1321,8 @@ u_int32_t sk_data_hi; u_int32_t sk_xmac_rxstat; u_int32_t sk_timestamp; - u_int16_t sk_csum2; - u_int16_t sk_csum1; - u_int16_t sk_csum2_start; - u_int16_t sk_csum1_start; + u_int32_t sk_csum; + u_int32_t sk_csum_start; }; #define SK_OPCODE_DEFAULT 0x00550000 @@ -1339,6 +1342,9 @@ #define SK_RXSTAT \ (SK_OPCODE_DEFAULT|SK_RXCTL_EOF_INTR|SK_RXCTL_LASTFRAG| \ SK_RXCTL_FIRSTFRAG|SK_RXCTL_OWN) +#define SK_CRXSTAT \ + (SK_OPCODE_CSUM|SK_RXCTL_EOF_INTR|SK_RXCTL_LASTFRAG| \ + SK_RXCTL_FIRSTFRAG|SK_RXCTL_OWN) struct sk_tx_desc { u_int32_t sk_ctl; @@ -1346,10 +1352,8 @@ u_int32_t sk_data_lo; u_int32_t sk_data_hi; u_int32_t sk_xmac_txstat; - u_int16_t sk_rsvd0; - u_int16_t sk_csum_startval; - u_int16_t sk_csum_startpos; - u_int16_t sk_csum_writepos; + u_int32_t sk_csum_startval; + u_int32_t sk_csum_start; u_int32_t sk_rsvd1; }; @@ -1367,11 +1371,14 @@ #define SK_TXSTAT \ (SK_OPCODE_DEFAULT|SK_TXCTL_EOF_INTR|SK_TXCTL_LASTFRAG|SK_TXCTL_OWN) -#define SK_RXBYTES(x) (x) & 0x0000FFFF; +#define SK_RXBYTES(x) ((x) & 0x0000FFFF) #define SK_TXBYTES SK_RXBYTES #define SK_TX_RING_CNT 512 #define SK_RX_RING_CNT 256 +#define SK_JUMBO_RX_RING_CNT 256 +#define SK_MAXTXSEGS 32 +#define SK_MAXRXSEGS 32 /* * Jumbo buffer stuff. Note that we must allocate more jumbo @@ -1383,6 +1390,8 @@ */ #define SK_JUMBO_FRAMELEN 9018 #define SK_JUMBO_MTU (SK_JUMBO_FRAMELEN-ETHER_HDR_LEN-ETHER_CRC_LEN) +#define SK_MAX_FRAMELEN (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) +#define SK_MIN_FRAMELEN (ETHER_MIN_LEN - ETHER_CRC_LEN) #define SK_JSLOTS ((SK_RX_RING_CNT * 3) / 2) #define SK_JRAWLEN (SK_JUMBO_FRAMELEN + ETHER_ALIGN) @@ -1397,32 +1406,73 @@ SLIST_ENTRY(sk_jpool_entry) jpool_entries; }; -struct sk_chain { - void *sk_desc; - struct mbuf *sk_mbuf; - struct sk_chain *sk_next; +struct sk_txdesc { + struct mbuf *tx_m; + bus_dmamap_t tx_dmamap; + STAILQ_ENTRY(sk_txdesc) tx_q; +}; + +STAILQ_HEAD(sk_txdq, sk_txdesc); + +struct sk_rxdesc { + struct mbuf *rx_m; + bus_dmamap_t rx_dmamap; }; struct sk_chain_data { - struct sk_chain sk_tx_chain[SK_TX_RING_CNT]; - struct sk_chain sk_rx_chain[SK_RX_RING_CNT]; + bus_dma_tag_t sk_parent_tag; + bus_dma_tag_t sk_tx_tag; + struct sk_txdesc sk_txdesc[SK_TX_RING_CNT]; + struct sk_txdq sk_txfreeq; + struct sk_txdq sk_txbusyq; + bus_dma_tag_t sk_rx_tag; + struct sk_rxdesc sk_rxdesc[SK_RX_RING_CNT]; + bus_dma_tag_t sk_tx_ring_tag; + bus_dma_tag_t sk_rx_ring_tag; + bus_dmamap_t sk_tx_ring_map; + bus_dmamap_t sk_rx_ring_map; + bus_dmamap_t sk_rx_sparemap; + bus_dma_tag_t sk_jumbo_rx_tag; + bus_dma_tag_t sk_jumbo_tag; + bus_dmamap_t sk_jumbo_map; + bus_dma_tag_t sk_jumbo_mtag; + caddr_t sk_jslots[SK_JSLOTS]; + struct sk_rxdesc sk_jumbo_rxdesc[SK_JUMBO_RX_RING_CNT]; + bus_dma_tag_t sk_jumbo_rx_ring_tag; + bus_dmamap_t sk_jumbo_rx_ring_map; + bus_dmamap_t sk_jumbo_rx_sparemap; int sk_tx_prod; int sk_tx_cons; int sk_tx_cnt; int sk_rx_prod; - int sk_rx_cons; - int sk_rx_cnt; - /* Stick the jumbo mem management stuff here too. */ - caddr_t sk_jslots[SK_JSLOTS]; - void *sk_jumbo_buf; - + int sk_jumbo_rx_prod; }; struct sk_ring_data { - struct sk_tx_desc sk_tx_ring[SK_TX_RING_CNT]; - struct sk_rx_desc sk_rx_ring[SK_RX_RING_CNT]; + struct sk_tx_desc *sk_tx_ring; + bus_addr_t sk_tx_ring_paddr; + struct sk_rx_desc *sk_rx_ring; + bus_addr_t sk_rx_ring_paddr; + struct sk_rx_desc *sk_jumbo_rx_ring; + bus_addr_t sk_jumbo_rx_ring_paddr; + void *sk_jumbo_buf; + bus_addr_t sk_jumbo_buf_paddr; }; +#define SK_TX_RING_ADDR(sc, i) \ + ((sc)->sk_rdata.sk_tx_ring_paddr + sizeof(struct sk_tx_desc) * (i)) +#define SK_RX_RING_ADDR(sc, i) \ + ((sc)->sk_rdata.sk_rx_ring_paddr + sizeof(struct sk_rx_desc) * (i)) +#define SK_JUMBO_RX_RING_ADDR(sc, i) \ + ((sc)->sk_rdata.sk_jumbo_rx_ring_paddr + sizeof(struct sk_rx_desc) * (i)) + +#define SK_TX_RING_SZ \ + (sizeof(struct sk_tx_desc) * SK_TX_RING_CNT) +#define SK_RX_RING_SZ \ + (sizeof(struct sk_rx_desc) * SK_RX_RING_CNT) +#define SK_JUMBO_RX_RING_SZ \ + (sizeof(struct sk_rx_desc) * SK_JUMBO_RX_RING_CNT) + struct sk_bcom_hack { int reg; int val; @@ -1440,7 +1490,7 @@ void *sk_intrhand; /* irq handler handle */ struct resource *sk_irq; /* IRQ resource handle */ struct resource *sk_res; /* I/O or shared mem handle */ - u_int8_t sk_unit; /* controller number */ + device_t sk_dev; u_int8_t sk_type; u_int8_t sk_rev; u_int8_t spare; @@ -1452,6 +1502,7 @@ u_int32_t sk_pmd; /* physical media type */ u_int32_t sk_intrmask; int sk_int_mod; + int sk_int_ticks; struct sk_if_softc *sk_if[2]; device_t sk_devs[2]; struct mtx sk_mtx; @@ -1468,7 +1519,7 @@ struct sk_if_softc { struct ifnet *sk_ifp; /* interface info */ device_t sk_miibus; - u_int8_t sk_unit; /* interface number */ + device_t sk_if_dev; u_int8_t sk_port; /* port # on controller */ u_int8_t sk_xmac_rev; /* XMAC chip rev (B2 or C1) */ u_int32_t sk_rx_ramstart; @@ -1477,12 +1528,10 @@ u_int32_t sk_tx_ramend; int sk_phytype; int sk_phyaddr; - device_t sk_dev; - int sk_cnt; int sk_link; - struct callout_handle sk_tick_ch; + struct callout sk_tick_ch; struct sk_chain_data sk_cdata; - struct sk_ring_data *sk_rdata; + struct sk_ring_data sk_rdata; struct sk_softc *sk_softc; /* parent controller */ int sk_tx_bmu; /* TX BMU register */ int sk_if_flags; @@ -1494,11 +1543,4 @@ #define SK_JLIST_LOCK(_sc) mtx_lock(&(_sc)->sk_jlist_mtx) #define SK_JLIST_UNLOCK(_sc) mtx_unlock(&(_sc)->sk_jlist_mtx) -#define SK_MAXUNIT 256 #define SK_TIMEOUT 1000 -#define ETHER_ALIGN 2 - -#ifdef __alpha__ -#undef vtophys -#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) -#endif