diff options
Diffstat (limited to 'cfe/cfe/arch/mips/board/bcm63xx_ram/src/dev_bcm63xx_eth.c')
-rwxr-xr-x | cfe/cfe/arch/mips/board/bcm63xx_ram/src/dev_bcm63xx_eth.c | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/cfe/cfe/arch/mips/board/bcm63xx_ram/src/dev_bcm63xx_eth.c b/cfe/cfe/arch/mips/board/bcm63xx_ram/src/dev_bcm63xx_eth.c new file mode 100755 index 0000000..c4ea465 --- /dev/null +++ b/cfe/cfe/arch/mips/board/bcm63xx_ram/src/dev_bcm63xx_eth.c @@ -0,0 +1,547 @@ +/* +<: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; +} |