15578Smx205022 /*
25578Smx205022 * CDDL HEADER START
35578Smx205022 *
45578Smx205022 * The contents of this file are subject to the terms of the
55578Smx205022 * Common Development and Distribution License (the "License").
65578Smx205022 * You may not use this file except in compliance with the License.
75578Smx205022 *
85578Smx205022 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95578Smx205022 * or http://www.opensolaris.org/os/licensing.
105578Smx205022 * See the License for the specific language governing permissions
115578Smx205022 * and limitations under the License.
125578Smx205022 *
135578Smx205022 * When distributing Covered Code, include this CDDL HEADER in each
145578Smx205022 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155578Smx205022 * If applicable, add the following below this CDDL HEADER, with the
165578Smx205022 * fields enclosed by brackets "[]" replaced with your own identifying
175578Smx205022 * information: Portions Copyright [yyyy] [name of copyright owner]
185578Smx205022 *
195578Smx205022 * CDDL HEADER END
205578Smx205022 */
215578Smx205022
225574Smx205022 /*
23*11878SVenu.Iyer@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
245574Smx205022 * Use is subject to license terms.
255574Smx205022 */
265574Smx205022
275574Smx205022 #include "nge.h"
285574Smx205022
295574Smx205022 #define TXD_OWN 0x80000000
305574Smx205022 #define TXD_ERR 0x40000000
315574Smx205022 #define TXD_END 0x20000000
325574Smx205022 #define TXD_BCNT_MSK 0x00003FFF
335574Smx205022
345574Smx205022
355574Smx205022 #undef NGE_DBG
365574Smx205022 #define NGE_DBG NGE_DBG_SEND
375574Smx205022
385574Smx205022 #define NGE_TXSWD_RECYCLE(sd) {\
395574Smx205022 (sd)->mp = NULL; \
405574Smx205022 (sd)->frags = 0; \
415574Smx205022 (sd)->mp_hndl.head = NULL; \
425574Smx205022 (sd)->mp_hndl.tail = NULL; \
435574Smx205022 (sd)->flags = HOST_OWN; \
445574Smx205022 }
455574Smx205022
465574Smx205022
475574Smx205022 static size_t nge_tx_dmah_pop(nge_dmah_list_t *, nge_dmah_list_t *, size_t);
485574Smx205022 static void nge_tx_dmah_push(nge_dmah_list_t *, nge_dmah_list_t *);
495574Smx205022
505574Smx205022
515574Smx205022 void nge_tx_recycle_all(nge_t *ngep);
525574Smx205022 #pragma no_inline(nge_tx_recycle_all)
535574Smx205022
545574Smx205022 void
nge_tx_recycle_all(nge_t * ngep)555574Smx205022 nge_tx_recycle_all(nge_t *ngep)
565574Smx205022 {
575574Smx205022 send_ring_t *srp;
585574Smx205022 sw_tx_sbd_t *ssbdp;
595574Smx205022 nge_dmah_node_t *dmah;
605574Smx205022 uint32_t slot;
615574Smx205022 uint32_t nslots;
625574Smx205022
635574Smx205022 srp = ngep->send;
645574Smx205022 nslots = srp->desc.nslots;
655574Smx205022
665574Smx205022 for (slot = 0; slot < nslots; ++slot) {
675574Smx205022
685574Smx205022 ssbdp = srp->sw_sbds + slot;
695574Smx205022
705574Smx205022 DMA_ZERO(ssbdp->desc);
715574Smx205022
725574Smx205022 if (ssbdp->mp != NULL) {
735574Smx205022
745574Smx205022 for (dmah = ssbdp->mp_hndl.head; dmah != NULL;
755574Smx205022 dmah = dmah->next)
765574Smx205022 (void) ddi_dma_unbind_handle(dmah->hndl);
775574Smx205022
785574Smx205022 freemsg(ssbdp->mp);
795574Smx205022 }
805574Smx205022
815574Smx205022 NGE_TXSWD_RECYCLE(ssbdp);
825574Smx205022 }
8310615SZhen.W@Sun.COM if (ngep->nge_mac_state == NGE_MAC_STARTED &&
8410615SZhen.W@Sun.COM ngep->resched_needed == 1) {
8510615SZhen.W@Sun.COM ngep->resched_needed = 0;
8610615SZhen.W@Sun.COM mac_tx_update(ngep->mh);
8710615SZhen.W@Sun.COM }
8810615SZhen.W@Sun.COM
895574Smx205022 }
905574Smx205022
915574Smx205022 static size_t
nge_tx_dmah_pop(nge_dmah_list_t * src,nge_dmah_list_t * dst,size_t num)925574Smx205022 nge_tx_dmah_pop(nge_dmah_list_t *src, nge_dmah_list_t *dst, size_t num)
935574Smx205022 {
945574Smx205022 nge_dmah_node_t *node;
955574Smx205022
965574Smx205022 for (node = src->head; node != NULL && --num != 0; node = node->next)
975574Smx205022 ;
985574Smx205022
995574Smx205022 if (num == 0) {
1005574Smx205022
1015574Smx205022 dst->head = src->head;
1025574Smx205022 dst->tail = node;
1035574Smx205022
1045574Smx205022 if ((src->head = node->next) == NULL)
1055574Smx205022 src->tail = NULL;
1065574Smx205022
1075574Smx205022 node->next = NULL;
1085574Smx205022 }
1095574Smx205022
1105574Smx205022 return (num);
1115574Smx205022 }
1125574Smx205022
1135574Smx205022 static void
nge_tx_dmah_push(nge_dmah_list_t * src,nge_dmah_list_t * dst)1145574Smx205022 nge_tx_dmah_push(nge_dmah_list_t *src, nge_dmah_list_t *dst)
1155574Smx205022 {
1165574Smx205022 if (dst->tail != NULL)
1175574Smx205022 dst->tail->next = src->head;
1185574Smx205022 else
1195574Smx205022 dst->head = src->head;
1205574Smx205022
1215574Smx205022 dst->tail = src->tail;
1225574Smx205022 }
1235574Smx205022
1245574Smx205022 static void
nge_tx_desc_sync(nge_t * ngep,uint32_t start_index,uint32_t bds,uint_t type)1257781SMin.Xu@Sun.COM nge_tx_desc_sync(nge_t *ngep, uint32_t start_index, uint32_t bds, uint_t type)
1265574Smx205022 {
1275574Smx205022 send_ring_t *srp = ngep->send;
1285574Smx205022 const size_t txd_size = ngep->desc_attr.txd_size;
1295574Smx205022 const uint64_t end = srp->desc.nslots * txd_size;
1307781SMin.Xu@Sun.COM uint64_t start;
1317781SMin.Xu@Sun.COM uint64_t num;
1325574Smx205022
1337781SMin.Xu@Sun.COM start = start_index * txd_size;
1347781SMin.Xu@Sun.COM num = bds * txd_size;
1355574Smx205022
1365574Smx205022 if (start + num <= end)
1375574Smx205022 (void) ddi_dma_sync(srp->desc.dma_hdl, start, num, type);
1385574Smx205022 else {
1395574Smx205022
1405574Smx205022 (void) ddi_dma_sync(srp->desc.dma_hdl, start, 0, type);
1415574Smx205022 (void) ddi_dma_sync(srp->desc.dma_hdl, 0, start + num - end,
1425574Smx205022 type);
1435574Smx205022 }
1445574Smx205022 }
1455574Smx205022
1465574Smx205022 /*
1475574Smx205022 * Reclaim the resource after tx's completion
1485574Smx205022 */
1495574Smx205022 void
nge_tx_recycle(nge_t * ngep,boolean_t is_intr)1505574Smx205022 nge_tx_recycle(nge_t *ngep, boolean_t is_intr)
1515574Smx205022 {
1525574Smx205022 int resched;
1535574Smx205022 uint32_t stflg;
1547781SMin.Xu@Sun.COM uint32_t free;
1557781SMin.Xu@Sun.COM uint32_t slot;
1567781SMin.Xu@Sun.COM uint32_t used;
1577781SMin.Xu@Sun.COM uint32_t next;
1587781SMin.Xu@Sun.COM uint32_t nslots;
1595574Smx205022 mblk_t *mp;
1605574Smx205022 sw_tx_sbd_t *ssbdp;
1615574Smx205022 void *hw_sbd_p;
1625574Smx205022 send_ring_t *srp;
1635574Smx205022 nge_dmah_node_t *dme;
1645574Smx205022 nge_dmah_list_t dmah;
1655574Smx205022
1665574Smx205022 srp = ngep->send;
1675574Smx205022
1685574Smx205022 if (is_intr) {
1695574Smx205022 if (mutex_tryenter(srp->tc_lock) == 0)
1705574Smx205022 return;
1715574Smx205022 } else
1725574Smx205022 mutex_enter(srp->tc_lock);
1735574Smx205022 mutex_enter(srp->tx_lock);
1745574Smx205022
1755574Smx205022 next = srp->tx_next;
1765574Smx205022 used = srp->tx_flow;
1775574Smx205022 free = srp->tx_free;
1785574Smx205022
1795574Smx205022 mutex_exit(srp->tx_lock);
1805574Smx205022
1815574Smx205022 slot = srp->tc_next;
1825574Smx205022 nslots = srp->desc.nslots;
1835574Smx205022
1845574Smx205022 used = nslots - free - used;
1855574Smx205022
1865574Smx205022 ASSERT(slot == NEXT_INDEX(next, free, nslots));
18710615SZhen.W@Sun.COM if (used == 0) {
18810615SZhen.W@Sun.COM ngep->watchdog = 0;
18910615SZhen.W@Sun.COM mutex_exit(srp->tc_lock);
19010615SZhen.W@Sun.COM return;
19110615SZhen.W@Sun.COM }
1925574Smx205022
19310615SZhen.W@Sun.COM if (used > srp->tx_hwmark && ngep->resched_needed == 0)
1945574Smx205022 used = srp->tx_hwmark;
1955574Smx205022
1965574Smx205022 nge_tx_desc_sync(ngep, slot, used, DDI_DMA_SYNC_FORKERNEL);
1975574Smx205022
1985574Smx205022 /*
1995574Smx205022 * Look through the send ring by bd's status part
2005574Smx205022 * to find all the bds which has been transmitted sucessfully
2015574Smx205022 * then reclaim all resouces associated with these bds
2025574Smx205022 */
2035574Smx205022
2045574Smx205022 mp = NULL;
2055574Smx205022 dmah.head = NULL;
2065574Smx205022 dmah.tail = NULL;
2075574Smx205022
2085574Smx205022 for (free = 0; used-- != 0; slot = NEXT(slot, nslots), ++free) {
2095574Smx205022
2105574Smx205022 ssbdp = &srp->sw_sbds[slot];
2115574Smx205022 hw_sbd_p = DMA_VPTR(ssbdp->desc);
2125574Smx205022
2138468SZhen.W@Sun.COM if (ssbdp->flags == HOST_OWN)
2145574Smx205022 break;
2159906SZhen.W@Sun.COM stflg = ngep->desc_attr.txd_check(hw_sbd_p);
2168468SZhen.W@Sun.COM if ((stflg & TXD_OWN) != 0)
2178468SZhen.W@Sun.COM break;
2188530SZhen.W@Sun.COM DMA_ZERO(ssbdp->desc);
2195574Smx205022 if (ssbdp->mp != NULL) {
2205574Smx205022 ssbdp->mp->b_next = mp;
2215574Smx205022 mp = ssbdp->mp;
2225574Smx205022
2235574Smx205022 if (ssbdp->mp_hndl.head != NULL)
2245574Smx205022 nge_tx_dmah_push(&ssbdp->mp_hndl, &dmah);
2255574Smx205022 }
2265574Smx205022
2275574Smx205022 NGE_TXSWD_RECYCLE(ssbdp);
2285574Smx205022 }
2295574Smx205022
2305574Smx205022 /*
2315574Smx205022 * We're about to release one or more places :-)
2325574Smx205022 * These ASSERTions check that our invariants still hold:
2335574Smx205022 * there must always be at least one free place
2345574Smx205022 * at this point, there must be at least one place NOT free
2355574Smx205022 * we're not about to free more places than were claimed!
2365574Smx205022 */
2375574Smx205022
23810615SZhen.W@Sun.COM if (free == 0) {
23910615SZhen.W@Sun.COM mutex_exit(srp->tc_lock);
24010615SZhen.W@Sun.COM return;
24110615SZhen.W@Sun.COM }
24210615SZhen.W@Sun.COM
2435574Smx205022 mutex_enter(srp->tx_lock);
2445574Smx205022
2455574Smx205022 srp->tx_free += free;
2465574Smx205022 ngep->watchdog = (srp->desc.nslots - srp->tx_free != 0);
2475574Smx205022
2485574Smx205022 srp->tc_next = slot;
2495574Smx205022
2505574Smx205022 ASSERT(srp->tx_free <= nslots);
2515574Smx205022 ASSERT(srp->tc_next == NEXT_INDEX(srp->tx_next, srp->tx_free, nslots));
2525574Smx205022
2535574Smx205022 resched = (ngep->resched_needed != 0 && srp->tx_hwmark <= srp->tx_free);
2545574Smx205022
2555574Smx205022 mutex_exit(srp->tx_lock);
2565574Smx205022 mutex_exit(srp->tc_lock);
2575574Smx205022
2585574Smx205022 /* unbind/free mblks */
2595574Smx205022
2605574Smx205022 for (dme = dmah.head; dme != NULL; dme = dme->next)
2615574Smx205022 (void) ddi_dma_unbind_handle(dme->hndl);
2628848SZhen.W@Sun.COM if (dmah.head != NULL) {
2638848SZhen.W@Sun.COM mutex_enter(&srp->dmah_lock);
2648848SZhen.W@Sun.COM nge_tx_dmah_push(&dmah, &srp->dmah_free);
2658848SZhen.W@Sun.COM mutex_exit(&srp->dmah_lock);
2668848SZhen.W@Sun.COM }
2675574Smx205022 freemsgchain(mp);
2685574Smx205022
2695574Smx205022 /*
2705574Smx205022 * up to this place, we maybe have reclaim some resouce
2715574Smx205022 * if there is a requirement to report to gld, report this.
2725574Smx205022 */
2735574Smx205022
2745574Smx205022 if (resched)
2755574Smx205022 (void) ddi_intr_trigger_softint(ngep->resched_hdl, NULL);
2765574Smx205022 }
2775574Smx205022
2787781SMin.Xu@Sun.COM static uint32_t
nge_tx_alloc(nge_t * ngep,uint32_t num)2797781SMin.Xu@Sun.COM nge_tx_alloc(nge_t *ngep, uint32_t num)
2805574Smx205022 {
2817781SMin.Xu@Sun.COM uint32_t start;
2825574Smx205022 send_ring_t *srp;
2835574Smx205022
2847781SMin.Xu@Sun.COM start = (uint32_t)-1;
2855574Smx205022 srp = ngep->send;
2865574Smx205022
2875574Smx205022 mutex_enter(srp->tx_lock);
2885574Smx205022
2895574Smx205022 if (srp->tx_free < srp->tx_lwmark) {
2905574Smx205022
2915574Smx205022 mutex_exit(srp->tx_lock);
2925574Smx205022 nge_tx_recycle(ngep, B_FALSE);
2935574Smx205022 mutex_enter(srp->tx_lock);
2945574Smx205022 }
2955574Smx205022
2965574Smx205022 if (srp->tx_free >= num) {
2975574Smx205022
2985574Smx205022 start = srp->tx_next;
2995574Smx205022
3005574Smx205022 srp->tx_next = NEXT_INDEX(start, num, srp->desc.nslots);
3015574Smx205022 srp->tx_free -= num;
3025574Smx205022 srp->tx_flow += num;
3035574Smx205022 }
3045574Smx205022
3055574Smx205022 mutex_exit(srp->tx_lock);
3065574Smx205022 return (start);
3075574Smx205022 }
3085574Smx205022
3095574Smx205022 static void
nge_tx_start(nge_t * ngep,uint32_t slotnum)3107781SMin.Xu@Sun.COM nge_tx_start(nge_t *ngep, uint32_t slotnum)
3115574Smx205022 {
3125574Smx205022 nge_mode_cntl mode_cntl;
3135574Smx205022 send_ring_t *srp;
3145574Smx205022
3155574Smx205022 srp = ngep->send;
3165574Smx205022
3175574Smx205022 /*
3185574Smx205022 * Because there can be multiple concurrent threads in
3195574Smx205022 * transit through this code, we only want to notify the
3205574Smx205022 * hardware once the last one is departing ...
3215574Smx205022 */
3225574Smx205022
3235574Smx205022 mutex_enter(srp->tx_lock);
3245574Smx205022
3255574Smx205022 srp->tx_flow -= slotnum;
3265574Smx205022 if (srp->tx_flow == 0) {
3275574Smx205022
3285574Smx205022 /*
3295574Smx205022 * Bump the watchdog counter, thus guaranteeing that it's
3305574Smx205022 * nonzero (watchdog activated). Note that non-synchonised
3315574Smx205022 * access here means we may race with the reclaim() code
3325574Smx205022 * above, but the outcome will be harmless. At worst, the
3335574Smx205022 * counter may not get reset on a partial reclaim; but the
3345574Smx205022 * large trigger threshold makes false positives unlikely
3355574Smx205022 */
33610615SZhen.W@Sun.COM if (ngep->watchdog == 0)
33710615SZhen.W@Sun.COM ngep->watchdog = 1;
3385574Smx205022
3395574Smx205022 mode_cntl.mode_val = nge_reg_get32(ngep, NGE_MODE_CNTL);
3405574Smx205022 mode_cntl.mode_bits.txdm = NGE_SET;
3415574Smx205022 mode_cntl.mode_bits.tx_rcom_en = NGE_SET;
3425574Smx205022 nge_reg_put32(ngep, NGE_MODE_CNTL, mode_cntl.mode_val);
3435574Smx205022 }
3445574Smx205022 mutex_exit(srp->tx_lock);
3455574Smx205022 }
3465574Smx205022
3475574Smx205022 static enum send_status
3485574Smx205022 nge_send_copy(nge_t *ngep, mblk_t *mp, send_ring_t *srp);
3495574Smx205022 #pragma inline(nge_send_copy)
3505574Smx205022
3515574Smx205022 static enum send_status
nge_send_copy(nge_t * ngep,mblk_t * mp,send_ring_t * srp)3525574Smx205022 nge_send_copy(nge_t *ngep, mblk_t *mp, send_ring_t *srp)
3535574Smx205022 {
3545574Smx205022 size_t totlen;
3555574Smx205022 size_t mblen;
3565574Smx205022 uint32_t flags;
3577781SMin.Xu@Sun.COM uint32_t bds;
3587781SMin.Xu@Sun.COM uint32_t start_index;
3595574Smx205022 char *txb;
3605574Smx205022 mblk_t *bp;
3615574Smx205022 void *hw_sbd_p;
3625574Smx205022 sw_tx_sbd_t *ssbdp;
36310615SZhen.W@Sun.COM boolean_t tfint;
3645574Smx205022
365*11878SVenu.Iyer@Sun.COM mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &flags);
3665574Smx205022 bds = 0x1;
3675574Smx205022
3687781SMin.Xu@Sun.COM if ((uint32_t)-1 == (start_index = nge_tx_alloc(ngep, bds)))
3695574Smx205022 return (SEND_COPY_FAIL);
3705574Smx205022
3715574Smx205022 ASSERT(start_index < srp->desc.nslots);
3725574Smx205022
3735574Smx205022 /*
3745574Smx205022 * up to this point, there's nothing that can fail,
3755574Smx205022 * so we can go straight to claiming our
3765574Smx205022 * already-reserved place son the train.
3775574Smx205022 *
3785574Smx205022 * This is the point of no return!
3795574Smx205022 */
3805574Smx205022
38110615SZhen.W@Sun.COM tfint = ((start_index % ngep->tfint_threshold) == 0);
3825574Smx205022 bp = mp;
3835574Smx205022 totlen = 0;
3845574Smx205022 ssbdp = &srp->sw_sbds[start_index];
3855574Smx205022 ASSERT(ssbdp->flags == HOST_OWN);
3865574Smx205022
3875574Smx205022 txb = DMA_VPTR(ssbdp->pbuf);
3885574Smx205022 totlen = 0;
3895574Smx205022 for (; bp != NULL; bp = bp->b_cont) {
3905574Smx205022 if ((mblen = MBLKL(bp)) == 0)
3915574Smx205022 continue;
3925574Smx205022 if ((totlen += mblen) <= ngep->max_sdu) {
3935574Smx205022 bcopy(bp->b_rptr, txb, mblen);
3945574Smx205022 txb += mblen;
3955574Smx205022 }
3965574Smx205022 }
3975574Smx205022
3985574Smx205022 DMA_SYNC(ssbdp->pbuf, DDI_DMA_SYNC_FORDEV);
3995574Smx205022
4005574Smx205022 /* Fill & sync hw desc */
4015574Smx205022
4025574Smx205022 hw_sbd_p = DMA_VPTR(ssbdp->desc);
4035574Smx205022
4045574Smx205022 ngep->desc_attr.txd_fill(hw_sbd_p, &ssbdp->pbuf.cookie, totlen,
40510615SZhen.W@Sun.COM flags, B_TRUE, tfint);
4065574Smx205022 nge_tx_desc_sync(ngep, start_index, bds, DDI_DMA_SYNC_FORDEV);
4075574Smx205022
4085574Smx205022 ssbdp->flags = CONTROLER_OWN;
4095574Smx205022
4105574Smx205022 nge_tx_start(ngep, bds);
4115574Smx205022
4125574Smx205022 /*
4135574Smx205022 * The return status indicates that the message can be freed
4145574Smx205022 * right away, as we've already copied the contents ...
4155574Smx205022 */
4165574Smx205022
4175574Smx205022 freemsg(mp);
4185574Smx205022 return (SEND_COPY_SUCESS);
4195574Smx205022 }
4205574Smx205022
4215574Smx205022 /*
4225574Smx205022 * static enum send_status
4235574Smx205022 * nge_send_mapped(nge_t *ngep, mblk_t *mp, size_t fragno);
4245574Smx205022 * #pragma inline(nge_send_mapped)
4255574Smx205022 */
4265574Smx205022
4275574Smx205022 static enum send_status
nge_send_mapped(nge_t * ngep,mblk_t * mp,size_t fragno)4285574Smx205022 nge_send_mapped(nge_t *ngep, mblk_t *mp, size_t fragno)
4295574Smx205022 {
4305574Smx205022 int err;
4315574Smx205022 boolean_t end;
4325574Smx205022 uint32_t i;
4335574Smx205022 uint32_t j;
4345574Smx205022 uint32_t ncookies;
4355574Smx205022 uint32_t slot;
4365574Smx205022 uint32_t nslots;
4375574Smx205022 uint32_t mblen;
4385574Smx205022 uint32_t flags;
4397781SMin.Xu@Sun.COM uint32_t start_index;
4407781SMin.Xu@Sun.COM uint32_t end_index;
4415574Smx205022 mblk_t *bp;
4425574Smx205022 void *hw_sbd_p;
4435574Smx205022 send_ring_t *srp;
4445574Smx205022 nge_dmah_node_t *dmah;
4455574Smx205022 nge_dmah_node_t *dmer;
4465574Smx205022 nge_dmah_list_t dmah_list;
4475574Smx205022 ddi_dma_cookie_t cookie[NGE_MAX_COOKIES * NGE_MAP_FRAGS];
44810615SZhen.W@Sun.COM boolean_t tfint;
4495574Smx205022
4505574Smx205022 srp = ngep->send;
4515574Smx205022 nslots = srp->desc.nslots;
4525574Smx205022
4535574Smx205022 mutex_enter(&srp->dmah_lock);
4545574Smx205022 err = nge_tx_dmah_pop(&srp->dmah_free, &dmah_list, fragno);
4555574Smx205022 mutex_exit(&srp->dmah_lock);
4565574Smx205022
4575574Smx205022 if (err != 0) {
4585574Smx205022
4595574Smx205022 return (SEND_MAP_FAIL);
4605574Smx205022 }
4615574Smx205022
4625574Smx205022 /*
4635574Smx205022 * Pre-scan the message chain, noting the total number of bytes,
4645574Smx205022 * the number of fragments by pre-doing dma addr bind
4655574Smx205022 * if the fragment is larger than NGE_COPY_SIZE.
4665574Smx205022 * This way has the following advantages:
4675574Smx205022 * 1. Acquire the detailed information of resouce
4685574Smx205022 * need to send the message
4695574Smx205022 *
4705574Smx205022 * 2. If can not pre-apply enough resouce, fails at once
4715574Smx205022 * and the driver will chose copy way to send out the
4725574Smx205022 * message
4735574Smx205022 */
4745574Smx205022
4755574Smx205022 slot = 0;
4765574Smx205022 dmah = dmah_list.head;
4775574Smx205022
478*11878SVenu.Iyer@Sun.COM mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &flags);
4795574Smx205022
4805574Smx205022 for (bp = mp; bp != NULL; bp = bp->b_cont) {
4815574Smx205022
4825574Smx205022 mblen = MBLKL(bp);
4835574Smx205022 if (mblen == 0)
4845574Smx205022 continue;
4855574Smx205022
4865574Smx205022 err = ddi_dma_addr_bind_handle(dmah->hndl,
4875574Smx205022 NULL, (caddr_t)bp->b_rptr, mblen,
4885574Smx205022 DDI_DMA_STREAMING | DDI_DMA_WRITE,
4895574Smx205022 DDI_DMA_DONTWAIT, NULL, cookie + slot, &ncookies);
4905574Smx205022
4915574Smx205022 /*
4925574Smx205022 * If there can not map successfully, it is uncessary
4935574Smx205022 * sending the message by map way. Sending the message
4945574Smx205022 * by copy way.
4955574Smx205022 *
4965574Smx205022 * By referring to intel's suggestion, it is better
4975574Smx205022 * the number of cookies should be less than 4.
4985574Smx205022 */
4995574Smx205022 if (err != DDI_DMA_MAPPED || ncookies > NGE_MAX_COOKIES) {
5005574Smx205022 NGE_DEBUG(("err(%x) map tx bulk fails"
5015574Smx205022 " cookie(%x), ncookies(%x)",
5025574Smx205022 err, cookie[slot].dmac_laddress, ncookies));
5035574Smx205022 goto map_fail;
5045574Smx205022 }
5055574Smx205022
5065574Smx205022 /*
5075574Smx205022 * Check How many bds a cookie will consume
5085574Smx205022 */
5095574Smx205022 for (end_index = slot + ncookies;
5105574Smx205022 ++slot != end_index;
5115574Smx205022 ddi_dma_nextcookie(dmah->hndl, cookie + slot))
5125574Smx205022 ;
5135574Smx205022
5145574Smx205022 dmah = dmah->next;
5155574Smx205022 }
5165574Smx205022
5175574Smx205022 /*
5185574Smx205022 * Now allocate tx descriptors and fill them
5195574Smx205022 * IMPORTANT:
5205574Smx205022 * Up to the point where it claims a place, It is impossibel
5215574Smx205022 * to fail.
5225574Smx205022 *
5235574Smx205022 * In this version, there's no setup to be done here, and there's
5245574Smx205022 * nothing that can fail, so we can go straight to claiming our
5255574Smx205022 * already-reserved places on the train.
5265574Smx205022 *
5275574Smx205022 * This is the point of no return!
5285574Smx205022 */
5295574Smx205022
5305574Smx205022
5317781SMin.Xu@Sun.COM if ((uint32_t)-1 == (start_index = nge_tx_alloc(ngep, slot)))
5325574Smx205022 goto map_fail;
5335574Smx205022
5345574Smx205022 ASSERT(start_index < nslots);
5355574Smx205022
5365574Smx205022 /* fill&sync hw desc, going in reverse order */
5375574Smx205022
5385574Smx205022 end = B_TRUE;
5395574Smx205022 end_index = NEXT_INDEX(start_index, slot - 1, nslots);
5405574Smx205022
5415574Smx205022 for (i = slot - 1, j = end_index; start_index - j != 0;
5425574Smx205022 j = PREV(j, nslots), --i) {
5435574Smx205022
54410615SZhen.W@Sun.COM tfint = ((j % ngep->tfint_threshold) == 0);
5455574Smx205022 hw_sbd_p = DMA_VPTR(srp->sw_sbds[j].desc);
5465574Smx205022 ngep->desc_attr.txd_fill(hw_sbd_p, cookie + i,
54710615SZhen.W@Sun.COM cookie[i].dmac_size, 0, end, tfint);
5485574Smx205022
5495574Smx205022 end = B_FALSE;
5505574Smx205022 }
5515574Smx205022
5525574Smx205022 hw_sbd_p = DMA_VPTR(srp->sw_sbds[j].desc);
55310615SZhen.W@Sun.COM tfint = ((j % ngep->tfint_threshold) == 0);
5545574Smx205022 ngep->desc_attr.txd_fill(hw_sbd_p, cookie + i, cookie[i].dmac_size,
55510615SZhen.W@Sun.COM flags, end, tfint);
5565574Smx205022
5575574Smx205022 nge_tx_desc_sync(ngep, start_index, slot, DDI_DMA_SYNC_FORDEV);
5585574Smx205022
5595574Smx205022 /* fill sw desc */
5605574Smx205022
5617781SMin.Xu@Sun.COM for (j = start_index; end_index - j != 0; j = NEXT(j, nslots)) {
5625574Smx205022
5635574Smx205022 srp->sw_sbds[j].flags = CONTROLER_OWN;
5645574Smx205022 }
5655574Smx205022
5665574Smx205022 srp->sw_sbds[j].mp = mp;
5675574Smx205022 srp->sw_sbds[j].mp_hndl = dmah_list;
5687781SMin.Xu@Sun.COM srp->sw_sbds[j].frags = (uint32_t)fragno;
5695574Smx205022 srp->sw_sbds[j].flags = CONTROLER_OWN;
5705574Smx205022
5715574Smx205022 nge_tx_start(ngep, slot);
5725574Smx205022
5735574Smx205022 /*
5745574Smx205022 * The return status indicates that the message can not be freed
5755574Smx205022 * right away, until we can make assure the message has been sent
5765574Smx205022 * out sucessfully.
5775574Smx205022 */
5785574Smx205022 return (SEND_MAP_SUCCESS);
5795574Smx205022
5805574Smx205022 map_fail:
5815574Smx205022 for (dmer = dmah_list.head; dmah - dmer != 0; dmer = dmer->next)
5825574Smx205022 (void) ddi_dma_unbind_handle(dmer->hndl);
5835574Smx205022
5845574Smx205022 mutex_enter(&srp->dmah_lock);
5855574Smx205022 nge_tx_dmah_push(&dmah_list, &srp->dmah_free);
5865574Smx205022 mutex_exit(&srp->dmah_lock);
5875574Smx205022
5885574Smx205022 return (SEND_MAP_FAIL);
5895574Smx205022 }
5905574Smx205022
5915574Smx205022 static boolean_t
nge_send(nge_t * ngep,mblk_t * mp)5925574Smx205022 nge_send(nge_t *ngep, mblk_t *mp)
5935574Smx205022 {
5945574Smx205022 mblk_t *bp;
5955574Smx205022 send_ring_t *srp;
5965574Smx205022 enum send_status status;
5975574Smx205022 uint32_t mblen = 0;
5985574Smx205022 uint32_t frags = 0;
5995574Smx205022 nge_statistics_t *nstp = &ngep->statistics;
6005574Smx205022 nge_sw_statistics_t *sw_stp = &nstp->sw_statistics;
6015574Smx205022
6025574Smx205022 ASSERT(mp != NULL);
6035574Smx205022 ASSERT(ngep->nge_mac_state == NGE_MAC_STARTED);
6045574Smx205022
6055574Smx205022 srp = ngep->send;
6065574Smx205022 /*
6075574Smx205022 * 1.Check the number of the fragments of the messages
6085574Smx205022 * If the total number is larger than 3,
6095574Smx205022 * Chose copy way
6105574Smx205022 *
6115574Smx205022 * 2. Check the length of the message whether is larger than
6125574Smx205022 * NGE_TX_COPY_SIZE, if so, choose the map way.
6135574Smx205022 */
6145574Smx205022 for (frags = 0, bp = mp; bp != NULL; bp = bp->b_cont) {
6155574Smx205022 if (MBLKL(bp) == 0)
6165574Smx205022 continue;
6175574Smx205022 frags++;
6185574Smx205022 mblen += MBLKL(bp);
6195574Smx205022 }
6205574Smx205022 if (mblen > (ngep->max_sdu) || mblen == 0) {
6215574Smx205022 freemsg(mp);
6225574Smx205022 return (B_TRUE);
6235574Smx205022 }
6245574Smx205022 if ((mblen > ngep->param_txbcopy_threshold) &&
6258848SZhen.W@Sun.COM (frags <= NGE_MAP_FRAGS) &&
6265574Smx205022 (srp->tx_free > frags * NGE_MAX_COOKIES)) {
6275574Smx205022 status = nge_send_mapped(ngep, mp, frags);
6285574Smx205022 if (status == SEND_MAP_FAIL)
6295574Smx205022 status = nge_send_copy(ngep, mp, srp);
6305574Smx205022 } else {
6315574Smx205022 status = nge_send_copy(ngep, mp, srp);
6325574Smx205022 }
6335574Smx205022 if (status == SEND_COPY_FAIL) {
6345574Smx205022 nge_tx_recycle(ngep, B_FALSE);
6355574Smx205022 status = nge_send_copy(ngep, mp, srp);
6365574Smx205022 if (status == SEND_COPY_FAIL) {
6375574Smx205022 ngep->resched_needed = 1;
6385574Smx205022 NGE_DEBUG(("nge_send: send fail!"));
6395574Smx205022 return (B_FALSE);
6405574Smx205022 }
6415574Smx205022 }
6425574Smx205022 /* Update the software statistics */
6435574Smx205022 sw_stp->obytes += mblen + ETHERFCSL;
6445574Smx205022 sw_stp->xmit_count ++;
6455574Smx205022
6465574Smx205022 return (B_TRUE);
6475574Smx205022 }
6485574Smx205022
6495574Smx205022 /*
6505574Smx205022 * nge_m_tx : Send a chain of packets.
6515574Smx205022 */
6525574Smx205022 mblk_t *
nge_m_tx(void * arg,mblk_t * mp)6535574Smx205022 nge_m_tx(void *arg, mblk_t *mp)
6545574Smx205022 {
6555574Smx205022 nge_t *ngep = arg;
6565574Smx205022 mblk_t *next;
6575574Smx205022
6585574Smx205022 rw_enter(ngep->rwlock, RW_READER);
6595574Smx205022 ASSERT(mp != NULL);
6605574Smx205022 if (ngep->nge_chip_state != NGE_CHIP_RUNNING) {
6615574Smx205022 freemsgchain(mp);
6625574Smx205022 mp = NULL;
6635574Smx205022 }
6645574Smx205022 while (mp != NULL) {
6655574Smx205022 next = mp->b_next;
6665574Smx205022 mp->b_next = NULL;
6675574Smx205022
6685574Smx205022 if (!nge_send(ngep, mp)) {
6695574Smx205022 mp->b_next = next;
6705574Smx205022 break;
6715574Smx205022 }
6725574Smx205022
6735574Smx205022 mp = next;
6745574Smx205022 }
6755574Smx205022 rw_exit(ngep->rwlock);
6765574Smx205022
6775574Smx205022 return (mp);
6785574Smx205022 }
6795574Smx205022
6805574Smx205022 /* ARGSUSED */
6815574Smx205022 uint_t
nge_reschedule(caddr_t args1,caddr_t args2)6825574Smx205022 nge_reschedule(caddr_t args1, caddr_t args2)
6835574Smx205022 {
6845574Smx205022 nge_t *ngep;
6855574Smx205022 uint_t rslt;
6865574Smx205022
6875574Smx205022 ngep = (nge_t *)args1;
6885574Smx205022 rslt = DDI_INTR_UNCLAIMED;
6895574Smx205022
6905574Smx205022 /*
6915574Smx205022 * when softintr is trigged, checking whether this
6925574Smx205022 * is caused by our expected interrupt
6935574Smx205022 */
6945574Smx205022 if (ngep->nge_mac_state == NGE_MAC_STARTED &&
6955574Smx205022 ngep->resched_needed == 1) {
6965574Smx205022 ngep->resched_needed = 0;
6975574Smx205022 ++ngep->statistics.sw_statistics.tx_resched;
6985574Smx205022 mac_tx_update(ngep->mh);
6995574Smx205022 rslt = DDI_INTR_CLAIMED;
7005574Smx205022 }
7015574Smx205022 return (rslt);
7025574Smx205022 }
7035574Smx205022
7045574Smx205022 uint32_t
nge_hot_txd_check(const void * hwd)7059906SZhen.W@Sun.COM nge_hot_txd_check(const void *hwd)
7065574Smx205022 {
7075574Smx205022 uint32_t err_flag;
7085574Smx205022 const hot_tx_bd * htbdp;
7095574Smx205022
7105574Smx205022 htbdp = hwd;
7119906SZhen.W@Sun.COM err_flag = htbdp->control_status.cntl_val;
7125574Smx205022 return (err_flag);
7135574Smx205022 }
7145574Smx205022
7155574Smx205022 uint32_t
nge_sum_txd_check(const void * hwd)7169906SZhen.W@Sun.COM nge_sum_txd_check(const void *hwd)
7175574Smx205022 {
7185574Smx205022 uint32_t err_flag;
7195574Smx205022 const sum_tx_bd * htbdp;
7205574Smx205022
7215574Smx205022 htbdp = hwd;
7229906SZhen.W@Sun.COM err_flag = htbdp->control_status.cntl_val;
7235574Smx205022 return (err_flag);
7245574Smx205022 }
7255574Smx205022
7265574Smx205022
7275574Smx205022 /*
7285574Smx205022 * Filling the contents of Tx's data descriptor
7295574Smx205022 * before transmitting.
7305574Smx205022 */
7315574Smx205022
7325574Smx205022 void
nge_hot_txd_fill(void * hwdesc,const ddi_dma_cookie_t * cookie,size_t length,uint32_t sum_flag,boolean_t end,boolean_t tfint)7335574Smx205022 nge_hot_txd_fill(void *hwdesc, const ddi_dma_cookie_t *cookie,
73410615SZhen.W@Sun.COM size_t length, uint32_t sum_flag, boolean_t end, boolean_t tfint)
7355574Smx205022 {
7365574Smx205022 hot_tx_bd * hw_sbd_p = hwdesc;
7375574Smx205022
7385574Smx205022 hw_sbd_p->host_buf_addr_hi = cookie->dmac_laddress >> 32;
7395574Smx205022 hw_sbd_p->host_buf_addr_lo = cookie->dmac_laddress;
7405574Smx205022
7415574Smx205022 /*
7425574Smx205022 * Setting the length of the packet
7435574Smx205022 * Note: the length filled in the part should be
7445574Smx205022 * the original length subtract 1;
7455574Smx205022 */
7465574Smx205022
7475574Smx205022 hw_sbd_p->control_status.control_sum_bits.bcnt = length - 1;
7485574Smx205022
7495574Smx205022 /* setting ip checksum */
7505574Smx205022 if (sum_flag & HCK_IPV4_HDRCKSUM)
7515574Smx205022 hw_sbd_p->control_status.control_sum_bits.ip_hsum
7525574Smx205022 = NGE_SET;
7535574Smx205022 /* setting tcp checksum */
7545574Smx205022 if (sum_flag & HCK_FULLCKSUM)
7555574Smx205022 hw_sbd_p->control_status.control_sum_bits.tcp_hsum
7565574Smx205022 = NGE_SET;
7575574Smx205022 /*
7585574Smx205022 * indicating the end of BDs
7595574Smx205022 */
76010615SZhen.W@Sun.COM if (tfint)
76110615SZhen.W@Sun.COM hw_sbd_p->control_status.control_sum_bits.inten = NGE_SET;
7625574Smx205022 if (end)
7635574Smx205022 hw_sbd_p->control_status.control_sum_bits.end = NGE_SET;
7645574Smx205022
7655574Smx205022 membar_producer();
7665574Smx205022
7675574Smx205022 /* pass desc to HW */
7685574Smx205022 hw_sbd_p->control_status.control_sum_bits.own = NGE_SET;
7695574Smx205022 }
7705574Smx205022
7715574Smx205022 void
nge_sum_txd_fill(void * hwdesc,const ddi_dma_cookie_t * cookie,size_t length,uint32_t sum_flag,boolean_t end,boolean_t tfint)7725574Smx205022 nge_sum_txd_fill(void *hwdesc, const ddi_dma_cookie_t *cookie,
77310615SZhen.W@Sun.COM size_t length, uint32_t sum_flag, boolean_t end, boolean_t tfint)
7745574Smx205022 {
7755574Smx205022 sum_tx_bd * hw_sbd_p = hwdesc;
7765574Smx205022
7775574Smx205022 hw_sbd_p->host_buf_addr = cookie->dmac_address;
7785574Smx205022
7795574Smx205022 /*
7805574Smx205022 * Setting the length of the packet
7815574Smx205022 * Note: the length filled in the part should be
7825574Smx205022 * the original length subtract 1;
7835574Smx205022 */
7845574Smx205022
7855574Smx205022 hw_sbd_p->control_status.control_sum_bits.bcnt = length - 1;
7865574Smx205022
7875574Smx205022 /* setting ip checksum */
7885574Smx205022 if (sum_flag & HCK_IPV4_HDRCKSUM)
7895574Smx205022 hw_sbd_p->control_status.control_sum_bits.ip_hsum
7905574Smx205022 = NGE_SET;
7915574Smx205022 /* setting tcp checksum */
7925574Smx205022 if (sum_flag & HCK_FULLCKSUM)
7935574Smx205022 hw_sbd_p->control_status.control_sum_bits.tcp_hsum
7945574Smx205022 = NGE_SET;
7955574Smx205022 /*
7965574Smx205022 * indicating the end of BDs
7975574Smx205022 */
79810615SZhen.W@Sun.COM if (tfint)
79910615SZhen.W@Sun.COM hw_sbd_p->control_status.control_sum_bits.inten = NGE_SET;
8005574Smx205022 if (end)
8015574Smx205022 hw_sbd_p->control_status.control_sum_bits.end = NGE_SET;
8025574Smx205022
8035574Smx205022 membar_producer();
8045574Smx205022
8055574Smx205022 /* pass desc to HW */
8065574Smx205022 hw_sbd_p->control_status.control_sum_bits.own = NGE_SET;
8075574Smx205022 }
808