xref: /onnv-gate/usr/src/uts/common/io/nge/nge_tx.c (revision 5574:9266e658617b)
1*5574Smx205022 /*
2*5574Smx205022  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3*5574Smx205022  * Use is subject to license terms.
4*5574Smx205022  */
5*5574Smx205022 
6*5574Smx205022 /*
7*5574Smx205022  * This file may contain confidential information of Nvidia
8*5574Smx205022  * and should not be distributed in source form without approval
9*5574Smx205022  * from Sun Legal.
10*5574Smx205022  */
11*5574Smx205022 
12*5574Smx205022 #pragma ident	"%Z%%M%	%I%	%E% SMI"
13*5574Smx205022 
14*5574Smx205022 #include "nge.h"
15*5574Smx205022 
16*5574Smx205022 #define	TXD_OWN		0x80000000
17*5574Smx205022 #define	TXD_ERR		0x40000000
18*5574Smx205022 #define	TXD_END		0x20000000
19*5574Smx205022 #define	TXD_BCNT_MSK	0x00003FFF
20*5574Smx205022 
21*5574Smx205022 
22*5574Smx205022 #undef	NGE_DBG
23*5574Smx205022 #define	NGE_DBG		NGE_DBG_SEND
24*5574Smx205022 
25*5574Smx205022 #define	NGE_TXSWD_RECYCLE(sd)	{\
26*5574Smx205022 					(sd)->mp = NULL; \
27*5574Smx205022 					(sd)->frags = 0; \
28*5574Smx205022 					(sd)->mp_hndl.head = NULL; \
29*5574Smx205022 					(sd)->mp_hndl.tail = NULL; \
30*5574Smx205022 					(sd)->flags = HOST_OWN; \
31*5574Smx205022 				}
32*5574Smx205022 
33*5574Smx205022 
34*5574Smx205022 static size_t nge_tx_dmah_pop(nge_dmah_list_t *, nge_dmah_list_t *, size_t);
35*5574Smx205022 static void nge_tx_dmah_push(nge_dmah_list_t *, nge_dmah_list_t *);
36*5574Smx205022 
37*5574Smx205022 
38*5574Smx205022 void nge_tx_recycle_all(nge_t *ngep);
39*5574Smx205022 #pragma	no_inline(nge_tx_recycle_all)
40*5574Smx205022 
41*5574Smx205022 void
42*5574Smx205022 nge_tx_recycle_all(nge_t *ngep)
43*5574Smx205022 {
44*5574Smx205022 	send_ring_t *srp;
45*5574Smx205022 	sw_tx_sbd_t *ssbdp;
46*5574Smx205022 	nge_dmah_node_t	*dmah;
47*5574Smx205022 	uint32_t slot;
48*5574Smx205022 	uint32_t nslots;
49*5574Smx205022 
50*5574Smx205022 	srp = ngep->send;
51*5574Smx205022 	nslots = srp->desc.nslots;
52*5574Smx205022 
53*5574Smx205022 	for (slot = 0; slot < nslots; ++slot) {
54*5574Smx205022 
55*5574Smx205022 		ssbdp = srp->sw_sbds + slot;
56*5574Smx205022 
57*5574Smx205022 		DMA_ZERO(ssbdp->desc);
58*5574Smx205022 
59*5574Smx205022 		if (ssbdp->mp != NULL)	{
60*5574Smx205022 
61*5574Smx205022 			for (dmah = ssbdp->mp_hndl.head; dmah != NULL;
62*5574Smx205022 			    dmah = dmah->next)
63*5574Smx205022 				(void) ddi_dma_unbind_handle(dmah->hndl);
64*5574Smx205022 
65*5574Smx205022 			freemsg(ssbdp->mp);
66*5574Smx205022 		}
67*5574Smx205022 
68*5574Smx205022 		NGE_TXSWD_RECYCLE(ssbdp);
69*5574Smx205022 	}
70*5574Smx205022 }
71*5574Smx205022 
72*5574Smx205022 static size_t
73*5574Smx205022 nge_tx_dmah_pop(nge_dmah_list_t *src, nge_dmah_list_t *dst, size_t num)
74*5574Smx205022 {
75*5574Smx205022 	nge_dmah_node_t	*node;
76*5574Smx205022 
77*5574Smx205022 	for (node = src->head; node != NULL && --num != 0; node = node->next)
78*5574Smx205022 		;
79*5574Smx205022 
80*5574Smx205022 	if (num == 0)	{
81*5574Smx205022 
82*5574Smx205022 		dst->head = src->head;
83*5574Smx205022 		dst->tail = node;
84*5574Smx205022 
85*5574Smx205022 		if ((src->head = node->next) == NULL)
86*5574Smx205022 			src->tail = NULL;
87*5574Smx205022 
88*5574Smx205022 		node->next = NULL;
89*5574Smx205022 	}
90*5574Smx205022 
91*5574Smx205022 	return (num);
92*5574Smx205022 }
93*5574Smx205022 
94*5574Smx205022 static void
95*5574Smx205022 nge_tx_dmah_push(nge_dmah_list_t *src, nge_dmah_list_t *dst)
96*5574Smx205022 {
97*5574Smx205022 	if (dst->tail != NULL)
98*5574Smx205022 		dst->tail->next = src->head;
99*5574Smx205022 	else
100*5574Smx205022 		dst->head = src->head;
101*5574Smx205022 
102*5574Smx205022 	dst->tail = src->tail;
103*5574Smx205022 }
104*5574Smx205022 
105*5574Smx205022 static void
106*5574Smx205022 nge_tx_desc_sync(nge_t *ngep, uint64_t start, uint64_t num, uint_t type)
107*5574Smx205022 {
108*5574Smx205022 	send_ring_t *srp = ngep->send;
109*5574Smx205022 	const size_t txd_size = ngep->desc_attr.txd_size;
110*5574Smx205022 	const uint64_t end = srp->desc.nslots * txd_size;
111*5574Smx205022 
112*5574Smx205022 	start = start * txd_size;
113*5574Smx205022 	num = num * txd_size;
114*5574Smx205022 
115*5574Smx205022 	if (start + num <= end)
116*5574Smx205022 		(void) ddi_dma_sync(srp->desc.dma_hdl, start, num, type);
117*5574Smx205022 	else	{
118*5574Smx205022 
119*5574Smx205022 		(void) ddi_dma_sync(srp->desc.dma_hdl, start, 0, type);
120*5574Smx205022 		(void) ddi_dma_sync(srp->desc.dma_hdl, 0, start + num - end,
121*5574Smx205022 		    type);
122*5574Smx205022 	}
123*5574Smx205022 }
124*5574Smx205022 
125*5574Smx205022 /*
126*5574Smx205022  * Reclaim the resource after tx's completion
127*5574Smx205022  */
128*5574Smx205022 void
129*5574Smx205022 nge_tx_recycle(nge_t *ngep, boolean_t is_intr)
130*5574Smx205022 {
131*5574Smx205022 	int resched;
132*5574Smx205022 	uint32_t stflg;
133*5574Smx205022 	size_t len;
134*5574Smx205022 	uint64_t free;
135*5574Smx205022 	uint64_t slot;
136*5574Smx205022 	uint64_t used;
137*5574Smx205022 	uint64_t next;
138*5574Smx205022 	uint64_t nslots;
139*5574Smx205022 	mblk_t *mp;
140*5574Smx205022 	sw_tx_sbd_t *ssbdp;
141*5574Smx205022 	void *hw_sbd_p;
142*5574Smx205022 	send_ring_t *srp;
143*5574Smx205022 	nge_dmah_node_t *dme;
144*5574Smx205022 	nge_dmah_list_t dmah;
145*5574Smx205022 
146*5574Smx205022 	srp = ngep->send;
147*5574Smx205022 
148*5574Smx205022 	if (is_intr) {
149*5574Smx205022 		if (mutex_tryenter(srp->tc_lock) == 0)
150*5574Smx205022 			return;
151*5574Smx205022 	} else
152*5574Smx205022 		mutex_enter(srp->tc_lock);
153*5574Smx205022 	mutex_enter(srp->tx_lock);
154*5574Smx205022 
155*5574Smx205022 	next = srp->tx_next;
156*5574Smx205022 	used = srp->tx_flow;
157*5574Smx205022 	free = srp->tx_free;
158*5574Smx205022 
159*5574Smx205022 	mutex_exit(srp->tx_lock);
160*5574Smx205022 
161*5574Smx205022 	slot = srp->tc_next;
162*5574Smx205022 	nslots = srp->desc.nslots;
163*5574Smx205022 
164*5574Smx205022 	used = nslots - free - used;
165*5574Smx205022 
166*5574Smx205022 	ASSERT(slot == NEXT_INDEX(next, free, nslots));
167*5574Smx205022 
168*5574Smx205022 	if (used > srp->tx_hwmark)
169*5574Smx205022 		used = srp->tx_hwmark;
170*5574Smx205022 
171*5574Smx205022 	nge_tx_desc_sync(ngep, slot, used, DDI_DMA_SYNC_FORKERNEL);
172*5574Smx205022 
173*5574Smx205022 	/*
174*5574Smx205022 	 * Look through the send ring by bd's status part
175*5574Smx205022 	 * to find all the bds which has been transmitted sucessfully
176*5574Smx205022 	 * then reclaim all resouces associated with these bds
177*5574Smx205022 	 */
178*5574Smx205022 
179*5574Smx205022 	mp = NULL;
180*5574Smx205022 	dmah.head = NULL;
181*5574Smx205022 	dmah.tail = NULL;
182*5574Smx205022 
183*5574Smx205022 	for (free = 0; used-- != 0; slot = NEXT(slot, nslots), ++free)	{
184*5574Smx205022 
185*5574Smx205022 		ssbdp = &srp->sw_sbds[slot];
186*5574Smx205022 		hw_sbd_p = DMA_VPTR(ssbdp->desc);
187*5574Smx205022 
188*5574Smx205022 		stflg = ngep->desc_attr.txd_check(hw_sbd_p, &len);
189*5574Smx205022 
190*5574Smx205022 		if (ssbdp->flags == HOST_OWN || (TXD_OWN & stflg) != 0)
191*5574Smx205022 			break;
192*5574Smx205022 
193*5574Smx205022 		DMA_ZERO(ssbdp->desc);
194*5574Smx205022 
195*5574Smx205022 		if (ssbdp->mp != NULL)	{
196*5574Smx205022 			ssbdp->mp->b_next = mp;
197*5574Smx205022 			mp = ssbdp->mp;
198*5574Smx205022 
199*5574Smx205022 			if (ssbdp->mp_hndl.head != NULL)
200*5574Smx205022 				nge_tx_dmah_push(&ssbdp->mp_hndl, &dmah);
201*5574Smx205022 		}
202*5574Smx205022 
203*5574Smx205022 		NGE_TXSWD_RECYCLE(ssbdp);
204*5574Smx205022 	}
205*5574Smx205022 
206*5574Smx205022 	/*
207*5574Smx205022 	 * We're about to release one or more places :-)
208*5574Smx205022 	 * These ASSERTions check that our invariants still hold:
209*5574Smx205022 	 * there must always be at least one free place
210*5574Smx205022 	 * at this point, there must be at least one place NOT free
211*5574Smx205022 	 * we're not about to free more places than were claimed!
212*5574Smx205022 	 */
213*5574Smx205022 
214*5574Smx205022 	mutex_enter(srp->tx_lock);
215*5574Smx205022 
216*5574Smx205022 	srp->tx_free += free;
217*5574Smx205022 	ngep->watchdog = (srp->desc.nslots - srp->tx_free != 0);
218*5574Smx205022 
219*5574Smx205022 	srp->tc_next = slot;
220*5574Smx205022 
221*5574Smx205022 	ASSERT(srp->tx_free <= nslots);
222*5574Smx205022 	ASSERT(srp->tc_next == NEXT_INDEX(srp->tx_next, srp->tx_free, nslots));
223*5574Smx205022 
224*5574Smx205022 	resched = (ngep->resched_needed != 0 && srp->tx_hwmark <= srp->tx_free);
225*5574Smx205022 
226*5574Smx205022 	mutex_exit(srp->tx_lock);
227*5574Smx205022 	mutex_exit(srp->tc_lock);
228*5574Smx205022 
229*5574Smx205022 	/* unbind/free mblks */
230*5574Smx205022 
231*5574Smx205022 	for (dme = dmah.head; dme != NULL; dme = dme->next)
232*5574Smx205022 		(void) ddi_dma_unbind_handle(dme->hndl);
233*5574Smx205022 
234*5574Smx205022 	mutex_enter(&srp->dmah_lock);
235*5574Smx205022 	nge_tx_dmah_push(&dmah, &srp->dmah_free);
236*5574Smx205022 	mutex_exit(&srp->dmah_lock);
237*5574Smx205022 
238*5574Smx205022 	freemsgchain(mp);
239*5574Smx205022 
240*5574Smx205022 	/*
241*5574Smx205022 	 * up to this place, we maybe have reclaim some resouce
242*5574Smx205022 	 * if there is a requirement to report to gld, report this.
243*5574Smx205022 	 */
244*5574Smx205022 
245*5574Smx205022 	if (resched)
246*5574Smx205022 		(void) ddi_intr_trigger_softint(ngep->resched_hdl, NULL);
247*5574Smx205022 }
248*5574Smx205022 
249*5574Smx205022 static uint64_t
250*5574Smx205022 nge_tx_alloc(nge_t *ngep, uint64_t num)
251*5574Smx205022 {
252*5574Smx205022 	uint64_t start;
253*5574Smx205022 	send_ring_t *srp;
254*5574Smx205022 
255*5574Smx205022 	start = (uint64_t)-1;
256*5574Smx205022 	srp = ngep->send;
257*5574Smx205022 
258*5574Smx205022 	mutex_enter(srp->tx_lock);
259*5574Smx205022 
260*5574Smx205022 	if (srp->tx_free < srp->tx_lwmark)	{
261*5574Smx205022 
262*5574Smx205022 		mutex_exit(srp->tx_lock);
263*5574Smx205022 		nge_tx_recycle(ngep, B_FALSE);
264*5574Smx205022 		mutex_enter(srp->tx_lock);
265*5574Smx205022 	}
266*5574Smx205022 
267*5574Smx205022 	if (srp->tx_free >= num)	{
268*5574Smx205022 
269*5574Smx205022 		start = srp->tx_next;
270*5574Smx205022 
271*5574Smx205022 		srp->tx_next = NEXT_INDEX(start, num, srp->desc.nslots);
272*5574Smx205022 		srp->tx_free -= num;
273*5574Smx205022 		srp->tx_flow += num;
274*5574Smx205022 	}
275*5574Smx205022 
276*5574Smx205022 	mutex_exit(srp->tx_lock);
277*5574Smx205022 	return (start);
278*5574Smx205022 }
279*5574Smx205022 
280*5574Smx205022 static void
281*5574Smx205022 nge_tx_start(nge_t *ngep, uint64_t slotnum)
282*5574Smx205022 {
283*5574Smx205022 	nge_mode_cntl mode_cntl;
284*5574Smx205022 	send_ring_t *srp;
285*5574Smx205022 
286*5574Smx205022 	srp = ngep->send;
287*5574Smx205022 
288*5574Smx205022 	/*
289*5574Smx205022 	 * Because there can be multiple concurrent threads in
290*5574Smx205022 	 * transit through this code, we only want to notify the
291*5574Smx205022 	 * hardware once the last one is departing ...
292*5574Smx205022 	 */
293*5574Smx205022 
294*5574Smx205022 	mutex_enter(srp->tx_lock);
295*5574Smx205022 
296*5574Smx205022 	srp->tx_flow -= slotnum;
297*5574Smx205022 	if (srp->tx_flow == 0) {
298*5574Smx205022 
299*5574Smx205022 		/*
300*5574Smx205022 		 * Bump the watchdog counter, thus guaranteeing that it's
301*5574Smx205022 		 * nonzero (watchdog activated).  Note that non-synchonised
302*5574Smx205022 		 * access here means we may race with the reclaim() code
303*5574Smx205022 		 * above, but the outcome will be harmless.  At worst, the
304*5574Smx205022 		 * counter may not get reset on a partial reclaim; but the
305*5574Smx205022 		 * large trigger threshold makes false positives unlikely
306*5574Smx205022 		 */
307*5574Smx205022 		ngep->watchdog ++;
308*5574Smx205022 
309*5574Smx205022 		mode_cntl.mode_val = nge_reg_get32(ngep, NGE_MODE_CNTL);
310*5574Smx205022 		mode_cntl.mode_bits.txdm = NGE_SET;
311*5574Smx205022 		mode_cntl.mode_bits.tx_rcom_en = NGE_SET;
312*5574Smx205022 		nge_reg_put32(ngep, NGE_MODE_CNTL, mode_cntl.mode_val);
313*5574Smx205022 	}
314*5574Smx205022 	mutex_exit(srp->tx_lock);
315*5574Smx205022 }
316*5574Smx205022 
317*5574Smx205022 static enum send_status
318*5574Smx205022 nge_send_copy(nge_t *ngep, mblk_t *mp, send_ring_t *srp);
319*5574Smx205022 #pragma	inline(nge_send_copy)
320*5574Smx205022 
321*5574Smx205022 static enum send_status
322*5574Smx205022 nge_send_copy(nge_t *ngep, mblk_t *mp, send_ring_t *srp)
323*5574Smx205022 {
324*5574Smx205022 	size_t totlen;
325*5574Smx205022 	size_t mblen;
326*5574Smx205022 	uint32_t flags;
327*5574Smx205022 	uint64_t bds;
328*5574Smx205022 	uint64_t start_index;
329*5574Smx205022 	char *txb;
330*5574Smx205022 	mblk_t *bp;
331*5574Smx205022 	void *hw_sbd_p;
332*5574Smx205022 	sw_tx_sbd_t *ssbdp;
333*5574Smx205022 
334*5574Smx205022 	hcksum_retrieve(mp, NULL, NULL, NULL, NULL,
335*5574Smx205022 	    NULL, NULL, &flags);
336*5574Smx205022 	bds = 0x1;
337*5574Smx205022 
338*5574Smx205022 	if ((uint64_t)-1 == (start_index = nge_tx_alloc(ngep, bds)))
339*5574Smx205022 		return (SEND_COPY_FAIL);
340*5574Smx205022 
341*5574Smx205022 	ASSERT(start_index < srp->desc.nslots);
342*5574Smx205022 
343*5574Smx205022 	/*
344*5574Smx205022 	 * up to this point, there's nothing that can fail,
345*5574Smx205022 	 * so we can go straight to claiming our
346*5574Smx205022 	 * already-reserved place son the train.
347*5574Smx205022 	 *
348*5574Smx205022 	 * This is the point of no return!
349*5574Smx205022 	 */
350*5574Smx205022 
351*5574Smx205022 	bp = mp;
352*5574Smx205022 	totlen = 0;
353*5574Smx205022 	ssbdp = &srp->sw_sbds[start_index];
354*5574Smx205022 	ASSERT(ssbdp->flags == HOST_OWN);
355*5574Smx205022 
356*5574Smx205022 	txb = DMA_VPTR(ssbdp->pbuf);
357*5574Smx205022 	totlen = 0;
358*5574Smx205022 	for (; bp != NULL; bp = bp->b_cont) {
359*5574Smx205022 		if ((mblen = MBLKL(bp)) == 0)
360*5574Smx205022 			continue;
361*5574Smx205022 		if ((totlen += mblen) <= ngep->max_sdu) {
362*5574Smx205022 			bcopy(bp->b_rptr, txb, mblen);
363*5574Smx205022 			txb += mblen;
364*5574Smx205022 		}
365*5574Smx205022 	}
366*5574Smx205022 
367*5574Smx205022 	DMA_SYNC(ssbdp->pbuf, DDI_DMA_SYNC_FORDEV);
368*5574Smx205022 
369*5574Smx205022 	/* Fill & sync hw desc */
370*5574Smx205022 
371*5574Smx205022 	hw_sbd_p = DMA_VPTR(ssbdp->desc);
372*5574Smx205022 
373*5574Smx205022 	ngep->desc_attr.txd_fill(hw_sbd_p, &ssbdp->pbuf.cookie, totlen,
374*5574Smx205022 	    flags, B_TRUE);
375*5574Smx205022 	nge_tx_desc_sync(ngep, start_index, bds, DDI_DMA_SYNC_FORDEV);
376*5574Smx205022 
377*5574Smx205022 	ssbdp->flags = CONTROLER_OWN;
378*5574Smx205022 
379*5574Smx205022 	nge_tx_start(ngep, bds);
380*5574Smx205022 
381*5574Smx205022 	/*
382*5574Smx205022 	 * The return status indicates that the message can be freed
383*5574Smx205022 	 * right away, as we've already copied the contents ...
384*5574Smx205022 	 */
385*5574Smx205022 
386*5574Smx205022 	freemsg(mp);
387*5574Smx205022 	return (SEND_COPY_SUCESS);
388*5574Smx205022 }
389*5574Smx205022 
390*5574Smx205022 /*
391*5574Smx205022  * static enum send_status
392*5574Smx205022  * nge_send_mapped(nge_t *ngep, mblk_t *mp, size_t fragno);
393*5574Smx205022  * #pragma	inline(nge_send_mapped)
394*5574Smx205022  */
395*5574Smx205022 
396*5574Smx205022 static enum send_status
397*5574Smx205022 nge_send_mapped(nge_t *ngep, mblk_t *mp, size_t fragno)
398*5574Smx205022 {
399*5574Smx205022 	int err;
400*5574Smx205022 	boolean_t end;
401*5574Smx205022 	uint32_t i;
402*5574Smx205022 	uint32_t j;
403*5574Smx205022 	uint32_t ncookies;
404*5574Smx205022 	uint32_t slot;
405*5574Smx205022 	uint32_t nslots;
406*5574Smx205022 	uint32_t mblen;
407*5574Smx205022 	uint32_t flags;
408*5574Smx205022 	uint64_t start_index;
409*5574Smx205022 	uint64_t end_index;
410*5574Smx205022 	mblk_t *bp;
411*5574Smx205022 	void *hw_sbd_p;
412*5574Smx205022 	send_ring_t *srp;
413*5574Smx205022 	nge_dmah_node_t *dmah;
414*5574Smx205022 	nge_dmah_node_t	*dmer;
415*5574Smx205022 	nge_dmah_list_t dmah_list;
416*5574Smx205022 	ddi_dma_cookie_t cookie[NGE_MAX_COOKIES * NGE_MAP_FRAGS];
417*5574Smx205022 
418*5574Smx205022 	srp = ngep->send;
419*5574Smx205022 	nslots = srp->desc.nslots;
420*5574Smx205022 
421*5574Smx205022 	mutex_enter(&srp->dmah_lock);
422*5574Smx205022 	err = nge_tx_dmah_pop(&srp->dmah_free, &dmah_list, fragno);
423*5574Smx205022 	mutex_exit(&srp->dmah_lock);
424*5574Smx205022 
425*5574Smx205022 	if (err != 0)	{
426*5574Smx205022 
427*5574Smx205022 		return (SEND_MAP_FAIL);
428*5574Smx205022 	}
429*5574Smx205022 
430*5574Smx205022 	/*
431*5574Smx205022 	 * Pre-scan the message chain, noting the total number of bytes,
432*5574Smx205022 	 * the number of fragments by pre-doing dma addr bind
433*5574Smx205022 	 * if the fragment is larger than NGE_COPY_SIZE.
434*5574Smx205022 	 * This way has the following advantages:
435*5574Smx205022 	 * 1. Acquire the detailed information of resouce
436*5574Smx205022 	 *	need to send the message
437*5574Smx205022 	 *
438*5574Smx205022 	 * 2. If can not pre-apply enough resouce, fails  at once
439*5574Smx205022 	 *	and the driver will chose copy way to send out the
440*5574Smx205022 	 *	message
441*5574Smx205022 	 */
442*5574Smx205022 
443*5574Smx205022 	slot = 0;
444*5574Smx205022 	dmah = dmah_list.head;
445*5574Smx205022 
446*5574Smx205022 	hcksum_retrieve(mp, NULL, NULL, NULL, NULL, NULL, NULL, &flags);
447*5574Smx205022 
448*5574Smx205022 	for (bp = mp; bp != NULL; bp = bp->b_cont)	{
449*5574Smx205022 
450*5574Smx205022 		mblen = MBLKL(bp);
451*5574Smx205022 		if (mblen == 0)
452*5574Smx205022 			continue;
453*5574Smx205022 
454*5574Smx205022 		err = ddi_dma_addr_bind_handle(dmah->hndl,
455*5574Smx205022 		    NULL, (caddr_t)bp->b_rptr, mblen,
456*5574Smx205022 		    DDI_DMA_STREAMING | DDI_DMA_WRITE,
457*5574Smx205022 		    DDI_DMA_DONTWAIT, NULL, cookie + slot, &ncookies);
458*5574Smx205022 
459*5574Smx205022 		/*
460*5574Smx205022 		 * If there can not map successfully, it is uncessary
461*5574Smx205022 		 * sending the message by map way. Sending the message
462*5574Smx205022 		 * by copy way.
463*5574Smx205022 		 *
464*5574Smx205022 		 * By referring to intel's suggestion, it is better
465*5574Smx205022 		 * the number of cookies should be less than 4.
466*5574Smx205022 		 */
467*5574Smx205022 		if (err != DDI_DMA_MAPPED || ncookies > NGE_MAX_COOKIES) {
468*5574Smx205022 			NGE_DEBUG(("err(%x) map tx bulk fails"
469*5574Smx205022 			    " cookie(%x), ncookies(%x)",
470*5574Smx205022 			    err, cookie[slot].dmac_laddress, ncookies));
471*5574Smx205022 			goto map_fail;
472*5574Smx205022 		}
473*5574Smx205022 
474*5574Smx205022 		/*
475*5574Smx205022 		 * Check How many bds a cookie will consume
476*5574Smx205022 		 */
477*5574Smx205022 		for (end_index = slot + ncookies;
478*5574Smx205022 		    ++slot != end_index;
479*5574Smx205022 		    ddi_dma_nextcookie(dmah->hndl, cookie + slot))
480*5574Smx205022 			;
481*5574Smx205022 
482*5574Smx205022 		dmah = dmah->next;
483*5574Smx205022 	}
484*5574Smx205022 
485*5574Smx205022 	/*
486*5574Smx205022 	 * Now allocate tx descriptors and fill them
487*5574Smx205022 	 * IMPORTANT:
488*5574Smx205022 	 *	Up to the point where it claims a place, It is impossibel
489*5574Smx205022 	 * 	to fail.
490*5574Smx205022 	 *
491*5574Smx205022 	 * In this version, there's no setup to be done here, and there's
492*5574Smx205022 	 * nothing that can fail, so we can go straight to claiming our
493*5574Smx205022 	 * already-reserved places on the train.
494*5574Smx205022 	 *
495*5574Smx205022 	 * This is the point of no return!
496*5574Smx205022 	 */
497*5574Smx205022 
498*5574Smx205022 
499*5574Smx205022 	if ((uint64_t)-1 == (start_index = nge_tx_alloc(ngep, slot)))
500*5574Smx205022 		goto map_fail;
501*5574Smx205022 
502*5574Smx205022 	ASSERT(start_index < nslots);
503*5574Smx205022 
504*5574Smx205022 	/* fill&sync hw desc, going in reverse order */
505*5574Smx205022 
506*5574Smx205022 	end = B_TRUE;
507*5574Smx205022 	end_index = NEXT_INDEX(start_index, slot - 1, nslots);
508*5574Smx205022 
509*5574Smx205022 	for (i = slot - 1, j = end_index; start_index - j != 0;
510*5574Smx205022 	    j = PREV(j, nslots), --i)	{
511*5574Smx205022 
512*5574Smx205022 		hw_sbd_p = DMA_VPTR(srp->sw_sbds[j].desc);
513*5574Smx205022 		ngep->desc_attr.txd_fill(hw_sbd_p, cookie + i,
514*5574Smx205022 		    cookie[i].dmac_size, 0, end);
515*5574Smx205022 
516*5574Smx205022 		end = B_FALSE;
517*5574Smx205022 	}
518*5574Smx205022 
519*5574Smx205022 	hw_sbd_p = DMA_VPTR(srp->sw_sbds[j].desc);
520*5574Smx205022 	ngep->desc_attr.txd_fill(hw_sbd_p, cookie + i, cookie[i].dmac_size,
521*5574Smx205022 	    flags, end);
522*5574Smx205022 
523*5574Smx205022 	nge_tx_desc_sync(ngep, start_index, slot, DDI_DMA_SYNC_FORDEV);
524*5574Smx205022 
525*5574Smx205022 	/* fill sw desc */
526*5574Smx205022 
527*5574Smx205022 	for (j = start_index; end_index - j != 0; j = NEXT(j, nslots))	{
528*5574Smx205022 
529*5574Smx205022 		srp->sw_sbds[j].flags = CONTROLER_OWN;
530*5574Smx205022 	}
531*5574Smx205022 
532*5574Smx205022 	srp->sw_sbds[j].mp = mp;
533*5574Smx205022 	srp->sw_sbds[j].mp_hndl = dmah_list;
534*5574Smx205022 	srp->sw_sbds[j].frags = fragno;
535*5574Smx205022 	srp->sw_sbds[j].flags = CONTROLER_OWN;
536*5574Smx205022 
537*5574Smx205022 	nge_tx_start(ngep, slot);
538*5574Smx205022 
539*5574Smx205022 	/*
540*5574Smx205022 	 * The return status indicates that the message can not be freed
541*5574Smx205022 	 * right away, until we can make assure the message has been sent
542*5574Smx205022 	 * out sucessfully.
543*5574Smx205022 	 */
544*5574Smx205022 	return (SEND_MAP_SUCCESS);
545*5574Smx205022 
546*5574Smx205022 map_fail:
547*5574Smx205022 	for (dmer = dmah_list.head; dmah - dmer != 0; dmer = dmer->next)
548*5574Smx205022 		(void) ddi_dma_unbind_handle(dmer->hndl);
549*5574Smx205022 
550*5574Smx205022 	mutex_enter(&srp->dmah_lock);
551*5574Smx205022 	nge_tx_dmah_push(&dmah_list, &srp->dmah_free);
552*5574Smx205022 	mutex_exit(&srp->dmah_lock);
553*5574Smx205022 
554*5574Smx205022 	return (SEND_MAP_FAIL);
555*5574Smx205022 }
556*5574Smx205022 
557*5574Smx205022 static boolean_t
558*5574Smx205022 nge_send(nge_t *ngep, mblk_t *mp)
559*5574Smx205022 {
560*5574Smx205022 	mblk_t *bp;
561*5574Smx205022 	send_ring_t *srp;
562*5574Smx205022 	enum send_status status;
563*5574Smx205022 	uint32_t mblen = 0;
564*5574Smx205022 	uint32_t frags = 0;
565*5574Smx205022 	nge_statistics_t *nstp = &ngep->statistics;
566*5574Smx205022 	nge_sw_statistics_t *sw_stp = &nstp->sw_statistics;
567*5574Smx205022 
568*5574Smx205022 	ASSERT(mp != NULL);
569*5574Smx205022 	ASSERT(ngep->nge_mac_state == NGE_MAC_STARTED);
570*5574Smx205022 
571*5574Smx205022 	srp = ngep->send;
572*5574Smx205022 	/*
573*5574Smx205022 	 * 1.Check the number of the fragments of the messages
574*5574Smx205022 	 * If the total number is larger than 3,
575*5574Smx205022 	 * Chose copy way
576*5574Smx205022 	 *
577*5574Smx205022 	 * 2. Check the length of the message whether is larger than
578*5574Smx205022 	 * NGE_TX_COPY_SIZE, if so, choose the map way.
579*5574Smx205022 	 */
580*5574Smx205022 	for (frags = 0, bp = mp; bp != NULL; bp = bp->b_cont) {
581*5574Smx205022 		if (MBLKL(bp) == 0)
582*5574Smx205022 			continue;
583*5574Smx205022 		frags++;
584*5574Smx205022 		mblen += MBLKL(bp);
585*5574Smx205022 	}
586*5574Smx205022 	if (mblen > (ngep->max_sdu) || mblen == 0) {
587*5574Smx205022 		freemsg(mp);
588*5574Smx205022 		return (B_TRUE);
589*5574Smx205022 	}
590*5574Smx205022 
591*5574Smx205022 	if ((mblen > ngep->param_txbcopy_threshold) &&
592*5574Smx205022 	    (srp->tx_free > frags * NGE_MAX_COOKIES)) {
593*5574Smx205022 		status = nge_send_mapped(ngep, mp, frags);
594*5574Smx205022 		if (status == SEND_MAP_FAIL)
595*5574Smx205022 			status = nge_send_copy(ngep, mp, srp);
596*5574Smx205022 	} else {
597*5574Smx205022 		status = nge_send_copy(ngep, mp, srp);
598*5574Smx205022 	}
599*5574Smx205022 	if (status == SEND_COPY_FAIL) {
600*5574Smx205022 		nge_tx_recycle(ngep, B_FALSE);
601*5574Smx205022 		status = nge_send_copy(ngep, mp, srp);
602*5574Smx205022 		if (status == SEND_COPY_FAIL) {
603*5574Smx205022 			ngep->resched_needed = 1;
604*5574Smx205022 			NGE_DEBUG(("nge_send: send fail!"));
605*5574Smx205022 			return (B_FALSE);
606*5574Smx205022 		}
607*5574Smx205022 	}
608*5574Smx205022 	/* Update the software statistics */
609*5574Smx205022 	sw_stp->obytes += mblen + ETHERFCSL;
610*5574Smx205022 	sw_stp->xmit_count ++;
611*5574Smx205022 
612*5574Smx205022 	return (B_TRUE);
613*5574Smx205022 }
614*5574Smx205022 
615*5574Smx205022 /*
616*5574Smx205022  * nge_m_tx : Send a chain of packets.
617*5574Smx205022  */
618*5574Smx205022 mblk_t *
619*5574Smx205022 nge_m_tx(void *arg, mblk_t *mp)
620*5574Smx205022 {
621*5574Smx205022 	nge_t *ngep = arg;
622*5574Smx205022 	mblk_t *next;
623*5574Smx205022 
624*5574Smx205022 	rw_enter(ngep->rwlock, RW_READER);
625*5574Smx205022 	ASSERT(mp != NULL);
626*5574Smx205022 	if (ngep->nge_chip_state != NGE_CHIP_RUNNING) {
627*5574Smx205022 		freemsgchain(mp);
628*5574Smx205022 		mp = NULL;
629*5574Smx205022 	}
630*5574Smx205022 	while (mp != NULL) {
631*5574Smx205022 		next = mp->b_next;
632*5574Smx205022 		mp->b_next = NULL;
633*5574Smx205022 
634*5574Smx205022 		if (!nge_send(ngep, mp)) {
635*5574Smx205022 			mp->b_next = next;
636*5574Smx205022 			break;
637*5574Smx205022 		}
638*5574Smx205022 
639*5574Smx205022 		mp = next;
640*5574Smx205022 	}
641*5574Smx205022 	rw_exit(ngep->rwlock);
642*5574Smx205022 
643*5574Smx205022 	return (mp);
644*5574Smx205022 }
645*5574Smx205022 
646*5574Smx205022 /* ARGSUSED */
647*5574Smx205022 uint_t
648*5574Smx205022 nge_reschedule(caddr_t args1, caddr_t args2)
649*5574Smx205022 {
650*5574Smx205022 	nge_t *ngep;
651*5574Smx205022 	uint_t rslt;
652*5574Smx205022 
653*5574Smx205022 	ngep = (nge_t *)args1;
654*5574Smx205022 	rslt = DDI_INTR_UNCLAIMED;
655*5574Smx205022 
656*5574Smx205022 	/*
657*5574Smx205022 	 * when softintr is trigged, checking whether this
658*5574Smx205022 	 * is caused by our expected interrupt
659*5574Smx205022 	 */
660*5574Smx205022 	if (ngep->nge_mac_state == NGE_MAC_STARTED &&
661*5574Smx205022 	    ngep->resched_needed == 1) {
662*5574Smx205022 		ngep->resched_needed = 0;
663*5574Smx205022 		++ngep->statistics.sw_statistics.tx_resched;
664*5574Smx205022 		mac_tx_update(ngep->mh);
665*5574Smx205022 		rslt = DDI_INTR_CLAIMED;
666*5574Smx205022 	}
667*5574Smx205022 	return (rslt);
668*5574Smx205022 }
669*5574Smx205022 
670*5574Smx205022 uint32_t
671*5574Smx205022 nge_hot_txd_check(const void *hwd, size_t *len)
672*5574Smx205022 {
673*5574Smx205022 	uint32_t err_flag;
674*5574Smx205022 	const hot_tx_bd * htbdp;
675*5574Smx205022 
676*5574Smx205022 	htbdp = hwd;
677*5574Smx205022 	err_flag = htbdp->control_status.cntl_val & ~TXD_BCNT_MSK;
678*5574Smx205022 
679*5574Smx205022 	*len = htbdp->control_status.status_bits.bcnt;
680*5574Smx205022 	return (err_flag);
681*5574Smx205022 }
682*5574Smx205022 
683*5574Smx205022 uint32_t
684*5574Smx205022 nge_sum_txd_check(const void *hwd, size_t *len)
685*5574Smx205022 {
686*5574Smx205022 	uint32_t err_flag;
687*5574Smx205022 	const sum_tx_bd * htbdp;
688*5574Smx205022 
689*5574Smx205022 	htbdp = hwd;
690*5574Smx205022 	err_flag = htbdp->control_status.cntl_val & ~TXD_BCNT_MSK;
691*5574Smx205022 
692*5574Smx205022 	*len = htbdp->control_status.status_bits.bcnt;
693*5574Smx205022 	return (err_flag);
694*5574Smx205022 }
695*5574Smx205022 
696*5574Smx205022 
697*5574Smx205022 /*
698*5574Smx205022  * Filling the contents of Tx's data descriptor
699*5574Smx205022  * before transmitting.
700*5574Smx205022  */
701*5574Smx205022 
702*5574Smx205022 void
703*5574Smx205022 nge_hot_txd_fill(void *hwdesc, const ddi_dma_cookie_t *cookie,
704*5574Smx205022 	size_t length, uint32_t sum_flag, boolean_t end)
705*5574Smx205022 {
706*5574Smx205022 	hot_tx_bd * hw_sbd_p = hwdesc;
707*5574Smx205022 
708*5574Smx205022 	hw_sbd_p->host_buf_addr_hi = cookie->dmac_laddress >> 32;
709*5574Smx205022 	hw_sbd_p->host_buf_addr_lo = cookie->dmac_laddress;
710*5574Smx205022 
711*5574Smx205022 	/*
712*5574Smx205022 	 * Setting the length of the packet
713*5574Smx205022 	 * Note: the length filled in the part should be
714*5574Smx205022 	 * the original length subtract 1;
715*5574Smx205022 	 */
716*5574Smx205022 
717*5574Smx205022 	hw_sbd_p->control_status.control_sum_bits.bcnt = length - 1;
718*5574Smx205022 
719*5574Smx205022 	/* setting ip checksum */
720*5574Smx205022 	if (sum_flag & HCK_IPV4_HDRCKSUM)
721*5574Smx205022 		hw_sbd_p->control_status.control_sum_bits.ip_hsum
722*5574Smx205022 		    = NGE_SET;
723*5574Smx205022 	/* setting tcp checksum */
724*5574Smx205022 	if (sum_flag & HCK_FULLCKSUM)
725*5574Smx205022 		hw_sbd_p->control_status.control_sum_bits.tcp_hsum
726*5574Smx205022 		    = NGE_SET;
727*5574Smx205022 	/*
728*5574Smx205022 	 * indicating the end of BDs
729*5574Smx205022 	 */
730*5574Smx205022 	if (end)
731*5574Smx205022 		hw_sbd_p->control_status.control_sum_bits.end = NGE_SET;
732*5574Smx205022 
733*5574Smx205022 	membar_producer();
734*5574Smx205022 
735*5574Smx205022 	/* pass desc to HW */
736*5574Smx205022 	hw_sbd_p->control_status.control_sum_bits.own = NGE_SET;
737*5574Smx205022 }
738*5574Smx205022 
739*5574Smx205022 void
740*5574Smx205022 nge_sum_txd_fill(void *hwdesc, const ddi_dma_cookie_t *cookie,
741*5574Smx205022 	size_t length, uint32_t sum_flag, boolean_t end)
742*5574Smx205022 {
743*5574Smx205022 	sum_tx_bd * hw_sbd_p = hwdesc;
744*5574Smx205022 
745*5574Smx205022 	hw_sbd_p->host_buf_addr = cookie->dmac_address;
746*5574Smx205022 
747*5574Smx205022 	/*
748*5574Smx205022 	 * Setting the length of the packet
749*5574Smx205022 	 * Note: the length filled in the part should be
750*5574Smx205022 	 * the original length subtract 1;
751*5574Smx205022 	 */
752*5574Smx205022 
753*5574Smx205022 	hw_sbd_p->control_status.control_sum_bits.bcnt = length - 1;
754*5574Smx205022 
755*5574Smx205022 	/* setting ip checksum */
756*5574Smx205022 	if (sum_flag & HCK_IPV4_HDRCKSUM)
757*5574Smx205022 		hw_sbd_p->control_status.control_sum_bits.ip_hsum
758*5574Smx205022 		    = NGE_SET;
759*5574Smx205022 	/* setting tcp checksum */
760*5574Smx205022 	if (sum_flag & HCK_FULLCKSUM)
761*5574Smx205022 		hw_sbd_p->control_status.control_sum_bits.tcp_hsum
762*5574Smx205022 		    = NGE_SET;
763*5574Smx205022 	/*
764*5574Smx205022 	 * indicating the end of BDs
765*5574Smx205022 	 */
766*5574Smx205022 	if (end)
767*5574Smx205022 		hw_sbd_p->control_status.control_sum_bits.end = NGE_SET;
768*5574Smx205022 
769*5574Smx205022 	membar_producer();
770*5574Smx205022 
771*5574Smx205022 	/* pass desc to HW */
772*5574Smx205022 	hw_sbd_p->control_status.control_sum_bits.own = NGE_SET;
773*5574Smx205022 }
774