📄 dev_sb1250_ethernet.c
字号:
unsigned int regval){ int mac_mdio_genc; sbeth_mii_sync(s); sbeth_mii_senddata(s,MII_COMMAND_START,2); sbeth_mii_senddata(s,MII_COMMAND_WRITE,2); sbeth_mii_senddata(s,phyaddr, 5); sbeth_mii_senddata(s,regidx, 5); sbeth_mii_senddata(s,MII_COMMAND_ACK,2); sbeth_mii_senddata(s,regval,16); mac_mdio_genc = SBETH_READCSR(s->sbe_mdio) & M_MAC_GENC; SBETH_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_OUTPUT | mac_mdio_genc);}/* ********************************************************************* * SBDMA_INITCHAN(s,d) * * Initialize the DMA channel, programming the CSRs to the * values calculated in the SBDMA_INITCTX routine. * * Input parameters: * s - sbeth structure * d - sbdma structure * * Return value: * nothing ********************************************************************* */static void sbdma_initchan(sbeth_t *s, sbethdma_t *d){ /* * Turn on the DMA channel */ SBETH_WRITECSR(d->sbdma_config1,0); SBETH_WRITECSR(d->sbdma_dscrbase,d->sbdma_dscrtable_phys); SBETH_WRITECSR(d->sbdma_config0, V_DMA_RINGSZ(d->sbdma_maxdescr) | 0);}/* ********************************************************************* * SBDMA_INITCTX(s,d,chan,txrx,maxdescr,callback) * * Initialize a DMA channel context. Since there are potentially * eight DMA channels per MAC, it's nice to do this in a standard * way. * * Input parameters: * s - sbeth_t structure (pointer to a MAC) * d - sbethdma_t structure (DMA channel context) * chan - channel number (0..1 right now) * txrx - Identifies DMA_TX or DMA_RX for channel direction * maxdescr - number of descriptors to allocate for the ring * * Return value: * nothing ********************************************************************* */static void sbdma_initctx(sbeth_t *s, sbethdma_t *d, int chan, int txrx, int maxdescr, void (*callback)(void *,int,void *,uint64_t,unsigned int)){ /* * Save away interesting stuff in the structure */ d->sbdma_eth = s; d->sbdma_channel = chan; d->sbdma_txdir = txrx; d->sbdma_maxdescr = maxdescr; /* * initialize register pointers */ d->sbdma_config0 = SBETH_PORT(s->sbe_baseaddr + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CONFIG0)); d->sbdma_config1 = SBETH_PORT(s->sbe_baseaddr + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CONFIG1)); d->sbdma_dscrbase = SBETH_PORT(s->sbe_baseaddr + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_DSCR_BASE)); d->sbdma_dscrcnt = SBETH_PORT(s->sbe_baseaddr + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_DSCR_CNT)); d->sbdma_curdscr = SBETH_PORT(s->sbe_baseaddr + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CUR_DSCRADDR)); /* * initialize the ring */ d->sbdma_dscrtable = (sbdmadscr_t *) KMALLOC(maxdescr*sizeof(sbdmadscr_t),sizeof(sbdmadscr_t)); d->sbdma_dscrtable_end = d->sbdma_dscrtable + maxdescr; d->sbdma_dscrtable_phys = SBETH_VTOP(d->sbdma_dscrtable); d->sbdma_addptr = d->sbdma_dscrtable; d->sbdma_remptr = d->sbdma_dscrtable; d->sbdma_ctxtable = (void **) KMALLOC(maxdescr*sizeof(void *),sizeof(void *)); /* * install callback */ d->sbdma_upcall = callback;}/* ********************************************************************* * SBDMA_RESET(d) * * Reset the software-maintained state for the specified * DMA channel. * * Input parameters: * d - dma channel * * Return value: * nothing ********************************************************************* */static void sbdma_reset(sbethdma_t *d){ d->sbdma_addptr = d->sbdma_dscrtable; d->sbdma_remptr = d->sbdma_dscrtable; d->sbdma_onring = 0;}/* ********************************************************************* * SBDMA_PROCBUFFERS(d,procfunc) * * Process "completed" buffers on the specified DMA channel. * This is normally called within the interrupt service routine. * Note that this isn't really ideal for priority channels, since * it processes all of the packets on a given channel before * returning. * * Input parameters: * d - DMA channel context * procfunc - routine to call for each completed buffer. This * is called with the context for the completed buffer, * the status from the descriptor, and the length from * the descriptor. * * Return value: * number of packets processed. ********************************************************************* */static int sbdma_procbuffers(sbethdma_t *d, void (*procfunc)(void *ifctx,int chan,void *ctx, uint64_t status, unsigned int pktlen)){ int curidx; int hwidx; int count = 0; sbdmadscr_t *dsc; for (;;) { /* * figure out where we are (as an index) and where * the hardware is (also as an index) * * This could be done faster if (for example) the * descriptor table was page-aligned and contiguous in * both virtual and physical memory -- you could then * just compare the low-order bits of the virtual address * (sbdma_remptr) and the physical address (sbdma_curdscr CSR) */ curidx = d->sbdma_remptr - d->sbdma_dscrtable; { uint64_t tmp; tmp = SBETH_READCSR(d->sbdma_curdscr); if (!tmp) { break; } hwidx = (int) (((tmp & M_DMA_CURDSCR_ADDR) - d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t)); } /* * If they're the same, that means we've processed all * of the descriptors up to (but not including) the one that * the hardware is working on right now. */ if (curidx == hwidx) break; /* * Remove packet from the on-ring count. */ d->sbdma_onring--; /* * Otherwise, issue the upcall. */ dsc = &(d->sbdma_dscrtable[curidx]); (*procfunc)(d->sbdma_eth->sbe_ifctx, d->sbdma_channel, d->sbdma_ctxtable[curidx], dsc->dscr_a & M_DMA_DSCRA_STATUS, (int)G_DMA_DSCRB_PKT_SIZE(dsc->dscr_b)); count++; /* * .. and advance to the next buffer. */ d->sbdma_remptr = sbdma_nextbuf(d,sbdma_remptr); } return count;}/* ********************************************************************* * SBDMA_ADDBUFFER(d,ptr,length,ctx) * * Add a buffer to the specified DMA channel. For transmit channels, * this causes a transmission to start. For receive channels, * this queues a buffer for inbound packets. * * Input parameters: * d - DMA channel descriptor * ptr - pointer to buffer (must by physically contiguous) * length - length of buffer * ctx - arbitrary data to be passed back when descriptor completes * (for example, mbuf pointers, etc.) * * Return value: * 0 if buffer could not be added (ring is full) * 1 if buffer added successfully ********************************************************************* */static int sbdma_addbuffer(sbethdma_t *d,uint8_t *ptr,int length,void *ctx){ sbdmadscr_t *dsc; sbdmadscr_t *nextdsc; sbeth_t *s = d->sbdma_eth; /* get pointer to our current place in the ring */ dsc = d->sbdma_addptr; nextdsc = sbdma_nextbuf(d,sbdma_addptr); /* * figure out if the ring is full - if the next descriptor * is the same as the one that we're going to remove from * the ring, the ring is full */ if (nextdsc == d->sbdma_remptr) { return 0; } /* * fill in the descriptor */ if (d->sbdma_txdir) { /* transmitting: set outbound options and length */ dsc->dscr_a = SBETH_VTOP(ptr) | V_DMA_DSCRA_A_SIZE(SBDMA_NUMCACHEBLKS(((uint64_t) length))) | M_DMA_DSCRA_INTERRUPT | M_DMA_ETHTX_SOP; if (s->fifo_mode) { dsc->dscr_b = V_DMA_DSCRB_OPTIONS(K_DMA_ETHTX_NOMODS) | V_DMA_DSCRB_PKT_SIZE(length); } else { dsc->dscr_b = V_DMA_DSCRB_OPTIONS(K_DMA_ETHTX_APPENDCRC_APPENDPAD) | V_DMA_DSCRB_PKT_SIZE(length); } } else { /* receiving: no options */ dsc->dscr_a = SBETH_VTOP(ptr) | V_DMA_DSCRA_A_SIZE(SBDMA_NUMCACHEBLKS(((uint64_t) length))) | M_DMA_DSCRA_INTERRUPT; dsc->dscr_b = 0; } /* * fill in the context */ d->sbdma_ctxtable[dsc-d->sbdma_dscrtable] = ctx; /* * point at next packet */ d->sbdma_addptr = nextdsc; /* * Give the packet to the hardware */ d->sbdma_onring++; SBETH_WRITECSR(d->sbdma_dscrcnt,1); return 1; /* we did it */}/* ********************************************************************* * SBETH_INITFREELIST(s) * * Initialize the buffer free list for this mac. The memory * allocated to the free list is carved up and placed on a linked * list of buffers for use by the mac. * * Input parameters: * s - sbeth structure * * Return value: * nothing ********************************************************************* */static void sbeth_initfreelist(sbeth_t *s){ int idx; unsigned char *ptr; sbeth_pkt_t *pkt; s->sbe_freelist = NULL; /* Must empty rxqueue, as we're about to free all the pkts on it */ s->sbe_rxqueue = NULL; ptr = s->sbe_pktpool; for (idx = 0; idx < SBETH_PKTPOOL_SIZE; idx++) { pkt = (sbeth_pkt_t *) ptr; sbeth_free_pkt(s,pkt); ptr += SBETH_PKTBUF_SIZE; }}/* ********************************************************************* * SBETH_ALLOC_PKT(s) * * Allocate a packet from the free list. * * Input parameters: * s - sbeth structure * * Return value: * pointer to packet structure, or NULL if none available ********************************************************************* */static sbeth_pkt_t *sbeth_alloc_pkt(sbeth_t *s){ uintptr_t addr; sbeth_pkt_t *pkt = s->sbe_freelist; if (!pkt) return NULL; s->sbe_freelist = pkt->next; pkt->next = NULL; addr = (uintptr_t) (pkt+1); if (addr & (SBDMA_CACHESIZE-1)) { addr = (addr + SBDMA_CACHESIZE) & ~(SBDMA_CACHESIZE-1); } pkt->buffer = (unsigned char *) addr; pkt->length = SBETH_PKT_SIZE; return pkt;}/* ********************************************************************* * SBETH_FREE_PKT(s,pkt) * * Return a packet to the free list * * Input parameters: * s - sbmac structure * pkt - packet to return * * Return value: * nothing ********************************************************************* */static void sbeth_free_pkt(sbeth_t *s,sbeth_pkt_t *pkt){ pkt->next = s->sbe_freelist; s->sbe_freelist = pkt;}/* ********************************************************************* * SBETH_TX_CALLBACK(ifctx,chan,ctx,status,pktsize) * * Transmit callback routine. This routine is invoked when a * queued transmit operation completes. In this simple driver, * all we do is free the packet and try to re-fill the receive ring. * * Input parameters: * ifctx - interface context (sbeth structure) * chan - DMA Channel * ctx - packet context (sbeth_pkt structure) * status - Ethernet status from descriptor * pktsize - length of packet (unused for transmits) * * Return value: * nothing ********************************************************************* */static void sbeth_tx_callback(void *ifctx,int chan,void *ctx, uint64_t status,unsigned int pktsize){ sbeth_t *s = ifctx; sbeth_pkt_t *pkt = ctx; sbeth_free_pkt(s,pkt); /* return packet to pool */ sbeth_fillrxring(s,chan); /* re-fill the receive ring */}/* ********************************************************************* * SBETH_RX_CALLBACK(ifctx,chan,ctx,status,pktsize) * * Receive callback routine. This routine is invoked when a * buffer queued for receives is filled. In this simple driver, * all we do is add the packet to a per-MAC queue for later * processing, and try to put a new packet in the place of the one * that was removed from the queue. * * Input parameters: * ifctx - interface context (sbeth structure) * chan - DMA Channel * ctx - packet context (sbeth_pkt structure) * status - Ethernet status from descriptor * pktsize - length of packet (unused for transmits) * * Return value: * nothing ********************************************************************* */static void sbeth_rx_callback(void *ifctx,int chan,void *ctx, uint64_t status,unsigned int pktsize){ sbeth_t *s = ifctx; sbeth_pkt_t *pkt = ctx; sbeth_pkt_t *listptr; if (!(status & M_DMA_ETHRX_BAD)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -