summaryrefslogtreecommitdiffstats
path: root/cfe/cfe/arch/mips/board/bcm63xx_ram/src/dev_bcm63xx_eth.c
diff options
context:
space:
mode:
Diffstat (limited to 'cfe/cfe/arch/mips/board/bcm63xx_ram/src/dev_bcm63xx_eth.c')
-rwxr-xr-xcfe/cfe/arch/mips/board/bcm63xx_ram/src/dev_bcm63xx_eth.c547
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;
+}