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