URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [devs/] [eth/] [arm/] [netarm/] [current/] [src/] [netarm_eth_drv.c] - Rev 851
Go to most recent revision | Compare with Previous | Blame | View Log
//========================================================================== // // netarm_eth_drv.c // // NetSilion NET+ARM Ethernet Driver (DMA driven) // //========================================================================== // ####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 2005 Free Software Foundation, Inc. // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 2 or (at your option) any later // version. // // eCos is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License // along with eCos; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // As a special exception, if other files instantiate templates or use // macros or inline functions from this file, or you compile this file // and link it with other works to produce a work based on this file, // this file does not by itself cause the resulting work to be covered by // the GNU General Public License. However the source code for this file // must still be made available in accordance with section (3) of the GNU // General Public License v2. // // This exception does not invalidate any other reasons why a work based // on this file might be covered by the GNU General Public License. // ------------------------------------------- // ####ECOSGPLCOPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): Harald Brandl (harald.brandl@fh-joanneum.at) // Contributors: Harald Brandl // Date: 01.08.2004 // Purpose: NET+ARM Ethernet Driver (DMA driven) // Description: // //####DESCRIPTIONEND#### // //========================================================================== #include <cyg/infra/cyg_type.h> #include <cyg/hal/hal_arch.h> #include <cyg/infra/diag.h> #include <cyg/hal/drv_api.h> #include <cyg/io/eth/netdev.h> #include <cyg/io/eth/eth_drv.h> #include <pkgconf/devs_eth_arm_netarm.h> #include <cyg/hal/hal_platform_ints.h> #include <cyg/hal/plf_mmap.h> #include "eth_regs.h" #include "MII.h" #include "netarm_eth_drv.h" #include "eeprom.h" #define BufferSizeA 128 #define BufferSizeB 512 #define BufferSizeC 1524 #define NumA 16 #define NumB 8 #define NumC 8 #define EEPROM_MAC 0 // location of MAC address inside eeprom static cyg_mutex_t Key_Mutex; static private_data_t driver_private; static unsigned char RxBufferA[NumA][BufferSizeA] __attribute__ ((aligned (4))); static unsigned char RxBufferB[NumB][BufferSizeB] __attribute__ ((aligned (4))); static unsigned char RxBufferC[NumC][BufferSizeC] __attribute__ ((aligned (4))); static unsigned char TxBuffer[1518] __attribute__ ((aligned (4))); static BDP_t RxBDP_A[NumA]; static BDP_t RxBDP_B[NumB]; static BDP_t RxBDP_C[NumC]; static BDP_t TxBDP; // relocation pointer for data accessed by DMA to enable caching static unsigned char *pRxBufferA = (unsigned char *)RxBufferA, *pRxBufferB = (unsigned char *)RxBufferB, *pRxBufferC = (unsigned char *)RxBufferC; static unsigned char *pTxBuffer = TxBuffer; static BDP_t *pRxBDP_A = RxBDP_A, *pRxBDP_B = RxBDP_B, *pRxBDP_C = RxBDP_C; static BDP_t *pTxBDP = &TxBDP; ETH_DRV_SC(netarm_sc, (void *)&driver_private, // driver specific data "eth0", // name for this interface netarm_start, netarm_stop, netarm_control, netarm_can_send, netarm_send, netarm_recv, netarm_deliver, netarm_poll, netarm_int_vector); NETDEVTAB_ENTRY(netarm_netdev, "ETH_DRV", netarm_init, &netarm_sc); #ifdef __thumb__ #define fastcopy memcpy #else static void fastcopy(void *buf, void *data, unsigned long len) { asm volatile( "STMDB SP!, {R11};" "TST R1, #2;" // test if aligned "LDRNEH R3, [R1], #2;" "STRNEH R3, [R0], #2;" "SUBNE R2, R2, #2;" "TST R1, #1;" "LDRNEB R3, [R1], #1;" "STRNEB R3, [R0], #1;" "SUBNE R2, R2, #1;" ".START%=:" "CMP R2, #44;" "BLT .LASTBYTES%=;" "LDMIA R1!, {R3 - R12, R14};" "STMIA R0!, {R3 - R12, R14};" "SUB R2, R2, #44;" "CMP R2, #44;" "BLT .LASTBYTES%=;" "LDMIA R1!, {R3 - R12, R14};" "STMIA R0!, {R3 - R12, R14};" "SUB R2, R2, #44;" "CMP R2, #44;" "BLT .LASTBYTES%=;" "LDMIA R1!, {R3 - R12, R14};" "STMIA R0!, {R3 - R12, R14};" "SUB R2, R2, #44;" "CMP R2, #44;" "BLT .LASTBYTES%=;" "LDMIA R1!, {R3 - R12, R14};" "STMIA R0!, {R3 - R12, R14};" "SUB R2, R2, #44;" "CMP R2, #44;" "BLT .LASTBYTES%=;" "LDMIA R1!, {R3 - R12, R14};" "STMIA R0!, {R3 - R12, R14};" "SUB R2, R2, #44;" "CMP R2, #44;" "BLT .LASTBYTES%=;" "LDMIA R1!, {R3 - R12, R14};" "STMIA R0!, {R3 - R12, R14};" "SUB R2, R2, #44;" "CMP R2, #44;" "BLT .LASTBYTES%=;" "LDMIA R1!, {R3 - R12, R14};" "STMIA R0!, {R3 - R12, R14};" "SUB R2, R2, #44;" "CMP R2, #44;" "BLT .LASTBYTES%=;" "LDMIA R1!, {R3 - R12, R14};" "STMIA R0!, {R3 - R12, R14};" "SUB R2, R2, #44;" "CMP R2, #44;" "BLT .LASTBYTES%=;" "LDMIA R1!, {R3 - R12, R14};" "STMIA R0!, {R3 - R12, R14};" "SUB R2, R2, #44;" "CMP R2, #44;" "BLT .LASTBYTES%=;" "LDMIA R1!, {R3 - R12, R14};" "STMIA R0!, {R3 - R12, R14};" "SUB R2, R2, #44;" "BGE .START%=;" ".LASTBYTES%=:" "AND R14, R2, #0xfffffffc;" "LDR PC, [PC, R14];" "NOP;" ".SWITCH%=:" ".word .CASE0%=;" ".word .CASE1%=;" ".word .CASE2%=;" ".word .CASE3%=;" ".word .CASE4%=;" ".word .CASE5%=;" ".word .CASE6%=;" ".word .CASE7%=;" ".word .CASE8%=;" ".word .CASE9%=;" ".word .CASE10%=;" ".CASE0%=:" "B .END%=;" ".CASE1%=:" "LDMIA R1!, {R3};" "STMIA R0!, {R3};" "B .END%=;" ".CASE2%=:" "LDMIA R1!, {R3, R4};" "STMIA R0!, {R3, R4};" "B .END%=;" ".CASE3%=:" "LDMIA R1!, {R3 - R5};" "STMIA R0!, {R3 - R5};" "B .END%=;" ".CASE4%=:" "LDMIA R1!, {R3 - R6};" "STMIA R0!, {R3 - R6};" "B .END%=;" ".CASE5%=:" "LDMIA R1!, {R3 - R7};" "STMIA R0!, {R3 - R7};" "B .END%=;" ".CASE6%=:" "LDMIA R1!, {R3 - R8};" "STMIA R0!, {R3 - R8};" "B .END%=;" ".CASE7%=:" "LDMIA R1!, {R3 - R9};" "STMIA R0!, {R3 - R9};" "B .END%=;" ".CASE8%=:" "LDMIA R1!, {R3 - R10};" "STMIA R0!, {R3 - R10};" "B .END%=;" ".CASE9%=:" "LDMIA R1!, {R3 - R11};" "STMIA R0!, {R3 - R11};" "B .END%=;" ".CASE10%=:" "LDMIA R1!, {R3 - R12};" "STMIA R0!, {R3 - R12};" ".END%=:" "TST R2, #2;" "LDRNEH R3, [R1], #2;" "STRNEH R3, [R0], #2;" "TST R2, #1;" "LDRNEB R3, [R1], #1;" "STRNEB R3, [R0], #1;" "LDMIA SP!, {R11};" : : "r" (buf), "r" (data), "r" (len) : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14" ); } #endif static bool KeyBufferFull(private_data_t *pd) { int tmp = 0; cyg_drv_mutex_lock(&Key_Mutex); if((pd->key_head + 1) % MaxKeys == pd->key_tail) { tmp = 1; } cyg_drv_mutex_unlock(&Key_Mutex); return tmp; } static void AddKey(unsigned long key, private_data_t *pd) { cyg_drv_mutex_lock(&Key_Mutex); pd->KeyBuffer[pd->key_head] = key; pd->key_head = (pd->key_head + 1) % MaxKeys; cyg_drv_mutex_unlock(&Key_Mutex); } static unsigned GetKey(private_data_t *pd) { unsigned key = 0; cyg_drv_mutex_lock(&Key_Mutex); if(pd->key_tail != pd->key_head) { key = pd->KeyBuffer[pd->key_tail]; pd->key_tail = (pd->key_tail + 1) % MaxKeys; } cyg_drv_mutex_unlock(&Key_Mutex); return key; } static cyg_uint32 dma_rx_isr(cyg_vector_t vector, cyg_addrword_t data) { // block this interrupt until the dsr completes cyg_drv_interrupt_mask(vector); // tell ecos to allow further interrupt processing cyg_drv_interrupt_acknowledge(vector); return CYG_ISR_CALL_DSR; // call the dsr } static void dma_rx_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) { eth_drv_dsr( vector, count, data ); HAL_WRITE_UINT32(DMA1A_SR, 0x80000000); // acknowledge and mask interrupts HAL_WRITE_UINT32(DMA1B_SR, 0x80000000); HAL_WRITE_UINT32(DMA1C_SR, 0x80000000); cyg_drv_interrupt_unmask(vector); } static cyg_uint32 dma_tx_isr(cyg_vector_t vector, cyg_addrword_t data) { // block this interrupt until the dsr completes cyg_drv_interrupt_mask(vector); // tell ecos to allow further interrupt processing cyg_drv_interrupt_acknowledge(vector); return CYG_ISR_CALL_DSR; // invoke the dsr } static void dma_tx_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) { eth_drv_dsr( vector, count, data ); HAL_OR_UINT32(DMA2_SR, 0x80000000); // acknowledge interrupt cyg_drv_interrupt_unmask(vector); } static void setupDMA(void) { int i; /* map DMA shared data to non-cached ram */ #ifdef CYGSEM_HAL_ENABLE_DCACHE_ON_STARTUP HAL_CACHED_TO_UNCACHED_ADDRESS(RxBufferA, pRxBufferA, unsigned char *); HAL_CACHED_TO_UNCACHED_ADDRESS(RxBufferB, pRxBufferB, unsigned char *); HAL_CACHED_TO_UNCACHED_ADDRESS(RxBufferC, pRxBufferC, unsigned char *); HAL_CACHED_TO_UNCACHED_ADDRESS(TxBuffer, pTxBuffer, unsigned char *); HAL_CACHED_TO_UNCACHED_ADDRESS(RxBDP_A, pRxBDP_A, BDP_t *); HAL_CACHED_TO_UNCACHED_ADDRESS(RxBDP_B, pRxBDP_B, BDP_t *); HAL_CACHED_TO_UNCACHED_ADDRESS(RxBDP_C, pRxBDP_C, BDP_t *); HAL_CACHED_TO_UNCACHED_ADDRESS(TxBDP, pTxBDP, BDP_t * ); #endif HAL_OR_UINT32(SYSCON, 0x40); // reset DMA module for(i = 0; i < NumA; i++) { pRxBDP_A[i].lo = ((unsigned)(pRxBufferA + i*BufferSizeA)) & 0x3fffffff; pRxBDP_A[i].hi = BufferSizeA; } pRxBDP_A[i - 1].lo |= 0x80000000; // set W bit for(i = 0; i < NumB; i++) { pRxBDP_B[i].lo = ((unsigned)(pRxBufferB + i*BufferSizeB)) & 0x3fffffff; pRxBDP_B[i].hi = BufferSizeB; } pRxBDP_B[i - 1].lo |= 0x80000000; for(i = 0; i < NumC; i++) { pRxBDP_C[i].lo = ((unsigned)(pRxBufferC + i*BufferSizeC)) & 0x3fffffff; pRxBDP_C[i].hi = BufferSizeC; } pRxBDP_C[i - 1].lo |= 0x80000000; HAL_AND_UINT32(SYSCON, ~0x40); HAL_WRITE_UINT32(DMA1A_BDP, (unsigned)pRxBDP_A); HAL_WRITE_UINT32(DMA1A_CR, 0x82000000); //burst transfer HAL_WRITE_UINT32(DMA1A_SR, 0xa00000); HAL_WRITE_UINT32(DMA1B_BDP, (unsigned)pRxBDP_B); HAL_WRITE_UINT32(DMA1B_CR, 0x82000000); //burst transfer HAL_WRITE_UINT32(DMA1B_SR, 0xa00000); HAL_WRITE_UINT32(DMA1C_BDP, (unsigned)pRxBDP_C); HAL_WRITE_UINT32(DMA1C_CR, 0x82000000); //burst transfer HAL_WRITE_UINT32(DMA1C_SR, 0xa00000); pTxBDP->lo = ((unsigned)pTxBuffer) & 0x3fffffff; pTxBDP->lo |= 0xa0000000; // set W and L bit HAL_WRITE_UINT32(DMA2_BDP, (unsigned)pTxBDP); HAL_WRITE_UINT32(DMA2_CR, 0x86000000); //burst transfer HAL_WRITE_UINT32(DMA2_SR, 0x800000); } static bool netarm_init(struct cyg_netdevtab_entry *tab) { struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance; cyg_bool duplex; static cyg_interrupt dma_rx_int_object, dma_tx_int_object; static cyg_handle_t dma_rx_int_handle, dma_tx_int_handle; #ifdef CYGSEM_DEVS_ETH_ARM_NETARM_ETH0_SET_ESA unsigned char esa[6] = CYGDAT_DEVS_ETH_ARM_NETARM_ETH0_ESA; #else unsigned char esa[6]; cyg_netarm_initI2C(); cyg_netarm_eepromRead(0x50, EEPROM_MAC, esa, 6); #endif // setup dma receiver setupDMA(); cyg_netarm_mii_reset(); cyg_netarm_mii_negotiate(); // initialize PHY duplex = cyg_netarm_mii_check_duplex(); // Ethernet Controller Initializatition // auto CRC, late collision retry HAL_WRITE_UINT32(MACCR, 0x1c | (duplex << 1)); // insert MAC source address into ethernet frame HAL_WRITE_UINT32(STLCR, 0x3); HAL_WRITE_UINT32(BtBIPGGapTimerR, 0x14); // standard values HAL_WRITE_UINT32(NonBtBIPGGapTimerR, (0x9 << 7) | 0x11);// standard values HAL_WRITE_UINT32(CollWinR, (0x37 << 8) | 0xf); // standard values HAL_WRITE_UINT32(SAFR, 0x1); // broadcast mode // dma mode, full duplex, enable pNA mode(needed for alignment) HAL_WRITE_UINT32(EthGenCR, 0x40400400 | (duplex << 16)); cyg_drv_interrupt_create( CYGNUM_HAL_INTERRUPT_DMA1, // Interrupt Vector 0, // Interrupt Priority (cyg_addrword_t)&netarm_sc, // Reference to Driver Instance dma_rx_isr, dma_rx_dsr, &dma_rx_int_handle, &dma_rx_int_object); cyg_drv_interrupt_create( CYGNUM_HAL_INTERRUPT_DMA2, // Interrupt Vector 0, // Interrupt Priority (cyg_addrword_t)&netarm_sc, // Reference to Driver Instance dma_tx_isr, dma_tx_dsr, &dma_tx_int_handle, &dma_tx_int_object); cyg_drv_interrupt_attach(dma_rx_int_handle); cyg_drv_interrupt_attach(dma_tx_int_handle); cyg_mutex_init(&Key_Mutex); sc->funs->eth_drv->init(sc, esa); return true; } static void netarm_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len) { int i, len; private_data_t *pd = (private_data_t *)(sc->driver_private); unsigned char *data, *buf = pd->RxBuffer; for (i = 0; i < sg_len; i++) { data = (unsigned char *)(sg_list[i].buf); len = sg_list[i].len; if(len) { if(i == 1) { buf += 2; } fastcopy(data, buf, len); buf += len; } } } static void netarm_deliver(struct eth_drv_sc *sc) { static int a = 0, b = 0, c = 0; unsigned key, rega, regb, regc; private_data_t *pd = (private_data_t *)(sc->driver_private); while((key = GetKey(pd))) { sc->funs->eth_drv->tx_done(sc, key, 0); } while(pRxBDP_A[a].hi & 0x8000) { pd->RxBuffer = (unsigned char *)(pRxBDP_A[a].lo & 0x1FFFFFFF); HAL_REORDER_BARRIER(); sc->funs->eth_drv->recv(sc, pRxBDP_A[a].hi & 0x7FFF); HAL_REORDER_BARRIER(); pRxBDP_A[a].hi = BufferSizeA; HAL_REORDER_BARRIER(); a = (a + 1) % NumA; } while(pRxBDP_B[b].hi & 0x8000) { pd->RxBuffer = (unsigned char *)(pRxBDP_B[b].lo & 0x1FFFFFFF); HAL_REORDER_BARRIER(); sc->funs->eth_drv->recv(sc, pRxBDP_B[b].hi & 0x7FFF); HAL_REORDER_BARRIER(); pRxBDP_B[b].hi = BufferSizeB; HAL_REORDER_BARRIER(); b = (b + 1) % NumB; } while(pRxBDP_C[c].hi & 0x8000) { pd->RxBuffer = (unsigned char *)(pRxBDP_C[c].lo & 0x1FFFFFFF); HAL_REORDER_BARRIER(); sc->funs->eth_drv->recv(sc, pRxBDP_C[c].hi & 0x7FFF); HAL_REORDER_BARRIER(); pRxBDP_C[c].hi = BufferSizeC; HAL_REORDER_BARRIER(); c = (c + 1) % NumC; } HAL_READ_UINT32(DMA1A_SR, rega); HAL_READ_UINT32(DMA1B_SR, regb); HAL_READ_UINT32(DMA1C_SR, regc); if((rega & 0x20000000) || (regb & 0x20000000) || (regc & 0x20000000)) { HAL_AND_UINT32(EthGenCR, ~0xc0000000); // reset Rx FIFO HAL_OR_UINT32(EthGenCR, 0xc0000000); } HAL_WRITE_UINT32(DMA1A_SR, 0x20a00000); HAL_WRITE_UINT32(DMA1B_SR, 0x20a00000); HAL_WRITE_UINT32(DMA1C_SR, 0x20a00000); } static int netarm_can_send(struct eth_drv_sc *sc) { private_data_t *pd = (private_data_t *)(sc->driver_private); if((pTxBDP->hi & 0x8000) || KeyBufferFull(pd)) return 0; return 1; } static void netarm_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total_len, unsigned long key) { private_data_t *pd = (private_data_t *)(sc->driver_private); int i, len; unsigned char *data, *buf = pTxBuffer; AddKey(key, pd); // Put data into buffer for(i = 0; i < sg_len; i++) { data = (unsigned char *)sg_list[i].buf; len = sg_list[i].len; if(len) { if(((unsigned)buf & 0x3) != ((unsigned)data & 0x3)) { memcpy(buf, data, len); } else { fastcopy(buf, data, len); } buf += len; } } cyg_drv_dsr_lock(); pTxBDP->hi = total_len | 0x8000; HAL_REORDER_BARRIER(); HAL_OR_UINT32(DMA2_SR, 0xf0000000); cyg_drv_dsr_unlock(); } static void netarm_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags) { setMAC(enaddr); // set MAC address HAL_REORDER_BARRIER(); HAL_OR_UINT32(EthGenCR, 0x80800000); // enable Rx und Tx FIFO HAL_REORDER_BARRIER(); cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_DMA1); cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_DMA2); } static void netarm_stop(struct eth_drv_sc *sc) { HAL_AND_UINT32(EthGenCR, 0x7f7fffff); HAL_REORDER_BARRIER(); cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_DMA1); cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_DMA2); } static int netarm_control(struct eth_drv_sc *sc, unsigned long key, void *data, int len) { return -1; } static void netarm_poll(struct eth_drv_sc *sc) { } static int netarm_int_vector(struct eth_drv_sc *sc) { return CYGNUM_HAL_INTERRUPT_DMA1; } static void setMAC(unsigned char *esa) { HAL_WRITE_UINT32(SAR1, (esa[1] << 8) | esa[0]); // set MAC address HAL_WRITE_UINT32(SAR2, (esa[3] << 8) | esa[2]); HAL_WRITE_UINT32(SAR3, (esa[5] << 8) | esa[4]); }
Go to most recent revision | Compare with Previous | Blame | View Log