/* <:copyright-broadcom Copyright (c) 2002 Broadcom Corporation All Rights Reserved No portions of this material may be reproduced in any form without the written permission of: Broadcom Corporation 16215 Alton Parkway Irvine, California 92619 All information contained in this document is Broadcom Corporation company private, proprietary, and trade secret. :> */ /** Includes. **/ #include "lib_types.h" #include "lib_malloc.h" #include "lib_string.h" #include "lib_printf.h" #include "cfe_iocb.h" #include "cfe_ioctl.h" #include "cfe_device.h" #include "cfe_devfuncs.h" #include "sbmips.h" #include "cfe_timer.h" #include "dev_bcm63xx_eth.h" #include "dev_bcm63xx_flash.h" #include "mii.h" #include "robosw_reg.h" #define DMA_RX_CHAN (softc->dmaPort * 2) #define DMA_TX_CHAN (softc->dmaPort * 2 + 1) #define CACHE_ALIGN 16 extern void _cfe_flushcache(int, uint8_t *, uint8_t *); #define INVAL_RANGE(s,l) _cfe_flushcache(CFE_CACHE_INVAL_RANGE,((uint8_t *) (s)),((uint8_t *) (s))+(l)) #define FLUSH_RANGE(s,l) _cfe_flushcache(CFE_CACHE_FLUSH_RANGE,((uint8_t *) (s)),((uint8_t *) (s))+(l)) /** Externs. **/ extern unsigned long sysGetEnetModeFlag(void); /** Prototypes. **/ static void bcm63xx_ether_probe( cfe_driver_t * drv, unsigned long probe_a, unsigned long probe_b, void * probe_ptr ); static int bcm63xx_ether_open(cfe_devctx_t *ctx); static int bcm63xx_ether_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); static int bcm63xx_ether_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); static int bcm63xx_ether_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); static int bcm63xx_ether_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); static int bcm63xx_ether_close(cfe_devctx_t *ctx); static int internal_open(bcmenet_softc * softc); /** Variables. **/ const static cfe_devdisp_t bcm63xx_ether_dispatch = { bcm63xx_ether_open, bcm63xx_ether_read, bcm63xx_ether_inpstat, bcm63xx_ether_write, bcm63xx_ether_ioctl, bcm63xx_ether_close, NULL, NULL }; const cfe_driver_t bcm63xx_enet = { "BCM63xx Ethernet", "eth", CFE_DEV_NETWORK, &bcm63xx_ether_dispatch, bcm63xx_ether_probe }; /** Functions. **/ static void bcm63xx_ether_probe( cfe_driver_t * drv, unsigned long probe_a, unsigned long probe_b, void * probe_ptr ) { bcmenet_softc * softc; char descr[100]; softc = (bcmenet_softc *) KMALLOC( sizeof(bcmenet_softc), CACHE_ALIGN ); if( softc == NULL ) { xprintf( "BCM63xx : Failed to allocate softc memory.\n" ); } else { memset( softc, 0, sizeof(bcmenet_softc) ); if (internal_open( softc ) == -1) xprintf("Failed initializing enet hardware\n"); else { cfe_attach( drv, softc, NULL, descr ); } } } static int bcm63xx_ether_open(cfe_devctx_t *ctx) { /* FIXME -- See if this can be returned to its normal place. */ return 0; } /* * init_dma: Initialize DMA control register */ static void init_dma(bcmenet_softc *softc) { uint32 *StateRam; int i; /* * clear State RAM */ StateRam = (UINT32 *)&softc->dmaCtrl->stram.s[0]; for (i = 0; i < sizeof(DmaStateRam) / sizeof(UINT32) * NUM_PORTS * 2; i++) StateRam[i] = 0; /* * initialize IUDMA controller register */ softc->dmaCtrl->controller_cfg = DMA_FLOWC_CH1_EN; softc->dmaCtrl->flowctl_ch1_thresh_lo = DMA_FC_THRESH_LO; softc->dmaCtrl->flowctl_ch1_thresh_hi = DMA_FC_THRESH_HI; softc->dmaCtrl->flowctl_ch1_alloc = 0; // transmit softc->txDma->cfg = 0; /* initialize first (will enable later) */ softc->txDma->maxBurst = DMA_MAX_BURST_LENGTH; softc->txDma->intMask = 0; /* mask all ints */ /* clr any pending interrupts on channel */ softc->txDma->intStat = DMA_DONE|DMA_NO_DESC|DMA_BUFF_DONE; softc->txDma->intMask = DMA_DONE; softc->dmaCtrl->stram.s[DMA_TX_CHAN].baseDescPtr = (uint32)K1_TO_PHYS((uint32_t)(softc->txFirstBdPtr)); // receive softc->rxDma->cfg = 0; // initialize first (will enable later) softc->rxDma->maxBurst = DMA_MAX_BURST_LENGTH; softc->rxDma->intMask = 0; /* mask all ints */ /* clr any pending interrupts on channel */ softc->rxDma->intStat = DMA_DONE|DMA_NO_DESC|DMA_BUFF_DONE; softc->rxDma->intMask = DMA_DONE; softc->dmaCtrl->stram.s[DMA_RX_CHAN].baseDescPtr = (uint32)K1_TO_PHYS((uint32_t)(softc->rxFirstBdPtr)); } static int internal_open(bcmenet_softc * softc) { int i; void *p; robosw_init(); softc->dmaCtrl = (DmaRegs *)(SWITCH_DMA_BASE); softc->rxDma = &softc->dmaCtrl->chcfg[DMA_RX_CHAN]; softc->txDma = &softc->dmaCtrl->chcfg[DMA_TX_CHAN]; // If doing SW reboot in EPI the controller can still be active softc->rxDma->cfg = 0; softc->txDma->cfg = 0; softc->dmaCtrl->controller_cfg &= ~DMA_MASTER_EN; p = KMALLOC( NR_TX_BDS * sizeof(DmaDesc), CACHE_ALIGN ); if( p == NULL ) { xprintf( "BCM63xx : Failed to allocate txBds memory.\n" ); return -1; } INVAL_RANGE(p, NR_TX_BDS * sizeof(DmaDesc)); softc->txBds = (DmaDesc *)K0_TO_K1((uint32) p); p = KMALLOC( NR_RX_BDS * sizeof(DmaDesc), CACHE_ALIGN ); if( p== NULL ) { xprintf( "BCM63xx : Failed to allocate rxBds memory.\n" ); KFREE( (void *)(softc->txBds) ); softc->txBds = NULL; return -1; } INVAL_RANGE(p, NR_RX_BDS * sizeof(DmaDesc)); softc->rxBds = (DmaDesc *)K0_TO_K1((uint32) p); softc->rxBuffers = (uint32_t)KMALLOC( NR_RX_BDS * ENET_BUF_SIZE, CACHE_ALIGN ); if( softc->rxBuffers == NULL ) { xprintf( "BCM63xx : Failed to allocate RxBuffer memory.\n" ); KFREE( (void *)(softc->txBds) ); softc->txBds = NULL; KFREE( (void *)(softc->rxBds) ); softc->rxBds = NULL; return -1; } INVAL_RANGE(softc->rxBuffers, NR_RX_BDS * ENET_BUF_SIZE); softc->txBuffers = (uint32_t)KMALLOC( NR_TX_BDS * ENET_BUF_SIZE, CACHE_ALIGN ); if( softc->txBuffers == NULL ) { xprintf( "BCM63xx : Failed to allocate txBuffer memory.\n" ); KFREE( (void *)(softc->rxBuffers) ); softc->rxBuffers = NULL; KFREE( (void *)(softc->txBds) ); softc->txBds = NULL; KFREE( (void *)(softc->rxBds) ); softc->rxBds = NULL; return -1; } INVAL_RANGE(softc->txBuffers, NR_TX_BDS * ENET_BUF_SIZE); /* Init the Receive Buffer Descriptor Ring. */ softc->rxFirstBdPtr = softc->rxBdReadPtr = softc->rxBds; softc->rxLastBdPtr = softc->rxBds + NR_RX_BDS - 1; for(i = 0; i < NR_RX_BDS; i++) { (softc->rxBds + i)->status = DMA_OWN; (softc->rxBds + i)->length = ENET_BUF_SIZE; (softc->rxBds + i)->address = softc->rxBuffers + i * ENET_BUF_SIZE; (softc->rxBds + i)->address = K1_TO_PHYS( (softc->rxBds + i)->address ); softc->dmaCtrl->flowctl_ch1_alloc = 1; } softc->rxLastBdPtr->status |= DMA_WRAP; /* Init Transmit Buffer Descriptor Ring. */ softc->txFirstBdPtr = softc->txNextBdPtr = softc->txBds; softc->txLastBdPtr = softc->txBds + NR_TX_BDS - 1; for(i = 0; i < NR_TX_BDS; i++) { (softc->txBds + i)->status = 0; (softc->txBds + i)->length = 0; (softc->txBds + i)->address = softc->txBuffers + i * ENET_BUF_SIZE; (softc->txBds + i)->address = K1_TO_PHYS( (softc->txBds + i)->address ); } softc->txLastBdPtr->status = DMA_WRAP; /* init dma registers */ init_dma(softc); robosw_configure_ports(); softc->dmaCtrl->controller_cfg |= DMA_MASTER_EN; softc->rxDma->cfg |= DMA_ENABLE; softc->linkCheck = 0; return 0; } static int bcm63xx_ether_read( cfe_devctx_t * ctx, iocb_buffer_t * buffer ) { unsigned char * dstptr; unsigned char * srcptr; volatile DmaDesc * CurrentBdPtr; bcmenet_softc * softc = (bcmenet_softc *) ctx->dev_softc; uint16 dmaFlag; if( ctx == NULL ) { xprintf( "No context\n" ); return -1; } if( buffer == NULL ) { xprintf( "No dst buffer\n" ); return -1; } if( softc == NULL ) { xprintf( "softc has not been initialized.\n" ); return -1; } dmaFlag = (uint16) softc->rxBdReadPtr->status; if (!(dmaFlag & DMA_EOP)) { xprintf("dmaFlag (return -1)[%04x]\n", dmaFlag); return -1; } dstptr = buffer->buf_ptr; CurrentBdPtr = softc->rxBdReadPtr; srcptr = (unsigned char *)( PHYS_TO_K1(CurrentBdPtr->address) ); buffer->buf_retlen = ((CurrentBdPtr->length < buffer->buf_length) ? CurrentBdPtr->length : buffer->buf_length); memcpy( dstptr, srcptr, buffer->buf_retlen ); CurrentBdPtr->length = ENET_BUF_SIZE; CurrentBdPtr->status &= DMA_WRAP; CurrentBdPtr->status |= DMA_OWN; IncRxBdPtr( CurrentBdPtr, softc ); softc->rxBdReadPtr = CurrentBdPtr; softc->dmaCtrl->flowctl_ch1_alloc = 1; // enable rx dma softc->rxDma->cfg = DMA_ENABLE; return 0; } static int bcm63xx_ether_inpstat( cfe_devctx_t * ctx, iocb_inpstat_t * inpstat ) { bcmenet_softc * softc; volatile DmaDesc * CurrentBdPtr; /* ============================= ASSERTIONS ============================= */ if( ctx == NULL ) { xprintf( "No context\n" ); return -1; } if( inpstat == NULL ) { xprintf( "No inpstat buffer\n" ); return -1; } softc = (bcmenet_softc *)ctx->dev_softc; if( softc == NULL ) { xprintf( "softc has not been initialized.\n" ); return -1; } /* ====================================================================== */ CurrentBdPtr = softc->rxBdReadPtr; // inp_status == 1 -> data available inpstat->inp_status = (CurrentBdPtr->status & DMA_OWN) ? 0 : 1; if (!inpstat->inp_status || (++softc->linkCheck > 100)) { // Don't check link state too often when receiving data softc->linkCheck = 0; robosw_check_ports(); } return 0; } static int bcm63xx_ether_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) { uint32_t status; unsigned char * dstptr; bcmenet_softc * softc; volatile DmaDesc * CurrentBdPtr; volatile uint32 txEvents = 0; /* ============================= ASSERTIONS ============================= */ if( ctx == NULL ) { xprintf( "No context\n" ); return -1; } if( buffer == NULL ) { xprintf( "No dst buffer\n" ); return -1; } if( buffer->buf_length > ENET_MAX_MTU_SIZE ) { xprintf( "src buffer too large.\n" ); xprintf( "size is %d\n", buffer->buf_length ); return -1; } softc = (bcmenet_softc *) ctx->dev_softc; if( softc == NULL ) { xprintf( "softc has not been initialized.\n" ); return -1; } /* ====================================================================== */ CurrentBdPtr = softc->txNextBdPtr; /* Find out if the next BD is available. */ if( CurrentBdPtr->status & DMA_OWN ) { xprintf( "No tx BD available ?!\n" ); return -1; } dstptr = (unsigned char *)PHYS_TO_K1( CurrentBdPtr->address ); memcpy( dstptr, buffer->buf_ptr, buffer->buf_length ); /* Set status of DMA BD to be transmitted. */ status = DMA_SOP | DMA_EOP | DMA_APPEND_CRC | DMA_OWN; if( CurrentBdPtr == softc->txLastBdPtr ) { status |= DMA_WRAP; } CurrentBdPtr->length = ((buffer->buf_length < ETH_ZLEN) ? ETH_ZLEN : buffer->buf_length); CurrentBdPtr->status = status; // Enable DMA for this channel softc->txDma->cfg |= DMA_ENABLE; // poll the dma status until done do { txEvents = CurrentBdPtr->status; } while (txEvents & DMA_OWN); //Advance BD pointer to next in the chain. InctxBdPtr( CurrentBdPtr, softc ); softc->txNextBdPtr = CurrentBdPtr; return 0; } static int bcm63xx_ether_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer) { bcmenet_softc *softc = (bcmenet_softc *) ctx->dev_softc; switch ((int)buffer->buf_ioctlcmd) { case IOCTL_ETHER_GETHWADDR: memcpy(buffer->buf_ptr, softc->hwaddr, sizeof(softc->hwaddr)); return 0; case IOCTL_ETHER_SETHWADDR: memcpy(softc->hwaddr, buffer->buf_ptr, sizeof(softc->hwaddr)); return 0; default: return -1; } return 0; } /* * bcm63xx_ether_flush: Flushes packets from the DMA receive ring */ static int bcm63xx_ether_flush(bcmenet_softc * softc) { volatile DmaDesc * CurrentBdPtr; uint16 dmaFlag; unsigned char * srcptr; uint16 len; uint32 rxBdsCount = 0; int i; if( softc == NULL ) { xprintf( "softc has not been initialized.\n" ); return -1; } xprintf("Disabling Switch ports.\n"); /* disable forwarding */ SWITCH->SwitchMode &= ~SwitchMode_FwdgEn; /* Bring the link down on all switch ports */ for(i=0; i<8; ++i) { SWITCH->PortOverride[i] &= ~PortOverride_Linkup; /* disable Rx and Tx */ SWITCH->PortCtrl[i] = PortCtrl_DisableTx | PortCtrl_DisableRx; } for(i=0; i<1000; ++i) { cfe_usleep(1000); } xprintf("Flushing Receive Buffers...\n"); while(!((dmaFlag = (uint16) softc->rxBdReadPtr->status) & DMA_OWN)) { if (!(dmaFlag & DMA_EOP)) { xprintf("dmaFlag (return -1)[%04x]\n", dmaFlag); return -1; } CurrentBdPtr = softc->rxBdReadPtr; srcptr = (unsigned char *)( PHYS_TO_K1(CurrentBdPtr->address) ); len = CurrentBdPtr->length; ++rxBdsCount; CurrentBdPtr->length = ENET_BUF_SIZE; CurrentBdPtr->status &= DMA_WRAP; CurrentBdPtr->status |= DMA_OWN; IncRxBdPtr( CurrentBdPtr, softc ); softc->rxBdReadPtr = CurrentBdPtr; softc->dmaCtrl->flowctl_ch1_alloc = 1; // enable rx dma softc->rxDma->cfg = DMA_ENABLE; } xprintf("%d buffers found.\n", rxBdsCount); return 0; } static int bcm63xx_ether_close(cfe_devctx_t *ctx) { int i; bcmenet_softc * softc = (bcmenet_softc *) ctx->dev_softc; unsigned long sts; /* flush Rx DMA Channel */ bcm63xx_ether_flush(softc); xprintf("Closing DMA Channels.\n"); sts = softc->rxDma->intStat; softc->rxDma->intStat = sts; softc->rxDma->intMask = 0; softc->rxDma->cfg = 0; // wait the current packet to complete before turning off EMAC, otherwise memory corruption can occur. for(i=0; softc->rxDma->cfg & DMA_ENABLE; i++) { // put the line below inside - it seems the signal can be lost and DMA never stops softc->rxDma->cfg = 0; if (i >= 20) { xprintf("Rx Timeout !!!\n"); break; } cfe_usleep(100); } sts = softc->txDma->intStat; softc->txDma->intStat = sts; softc->txDma->intMask = 0; softc->txDma->cfg = 0; for(i=0; softc->txDma->cfg & DMA_ENABLE; i++) { // put the line below inside - it seems the signal can be lost and DMA never stops softc->txDma->cfg = 0; if (i >= 20) { xprintf("Tx Timeout !!!\n"); break; } cfe_usleep(100); } /* return buffer allocation register internal count to 0 */ softc->dmaCtrl->flowctl_ch1_alloc = (DMA_BUF_ALLOC_FORCE | 0); softc->dmaCtrl->controller_cfg &= ~DMA_MASTER_EN; KFREE( (void *)(softc->txBuffers) ); KFREE( (void *)(softc->rxBuffers) ); KFREE( (void *)(softc->txBds) ); KFREE( (void *)(softc->rxBds) ); return 0; }