xref: /onnv-gate/usr/src/uts/common/io/nge/nge_tx.c (revision 11878:ac93462db6d7)
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