📄 if_ep93xx.c
字号:
//==========================================================================//// dev/if_ep93xx.c//// Ethernet device driver for Cirrus Logic EP93xx////==========================================================================//####COPYRIGHTBEGIN####// // ------------------------------------------- // The contents of this file are subject to the Red Hat eCos Public License // Version 1.1 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://www.redhat.com/ // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations under // the License. // // The Original Code is eCos - Embedded Configurable Operating System, // released September 30, 1998. // // The Initial Developer of the Original Code is Red Hat. // Portions created by Red Hat are // Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. // All Rights Reserved. // ------------------------------------------- // //####COPYRIGHTEND####//==========================================================================//#####DESCRIPTIONBEGIN####//// Author(s): // Contributors: // Date: // Purpose: // Description: hardware driver for EP9312 MAC// ////####DESCRIPTIONEND####////==========================================================================// Ethernet device driver for Cirrus Logic EP93xx#include <pkgconf/system.h>#include <pkgconf/devs_eth_arm_ep93xx.h>#ifdef CYGPKG_NET#include <pkgconf/net.h>#include <cyg/kernel/kapi.h>#endif#include <cyg/infra/cyg_type.h>#include <cyg/hal/hal_arch.h>#include <cyg/hal/hal_intr.h>#include <cyg/hal/hal_io.h>#include <cyg/hal/hal_diag.h>#include <cyg/infra/diag.h>#include <cyg/hal/drv_api.h>#include <cyg/hal/hal_cache.h>#include <cyg/io/eth/netdev.h>#include <cyg/io/eth/eth_drv.h>#include <cyg/hal/ep93xx.h>#include "ep93xx_eth.h" // ------------------------------------------------------------------------#ifndef CYGSEM_ARM_EP93XX_SET_ESA#ifdef CYGPKG_REDBOOT#include <pkgconf/redboot.h>#ifdef CYGSEM_REDBOOT_FLASH_CONFIG#include <redboot.h>#include <flash_config.h>RedBoot_config_option("Network hardware address [MAC]", ep93xx_esa, ALWAYS_ENABLED, true, CONFIG_ESA, 0x088812345678 );#endif#endif#endif// ------------------------------------------------------------------------extern void *memcpy( void *, const void *, unsigned long );// ------------------------------------------------------------------------//#define ETHDEBUG#ifdef ETHDEBUG#define dprintf diag_printf#else #define dprintf #endif // ETHDEBUG// ------------------------------------------------------------------------#define ETHER_ADDR_LEN 6#define ETHERNET_INTERRUPT (CYGNUM_HAL_INTERRUPT_MAC) // in case it changesstruct ep93xx_priv_data { // These are fixed, set-up-once, pointers to uncachable // addresses for the tables the device writes to: RxHdr *rxhdrs; RxStat *rxstat; TxHdr *txhdrs; TxStat *txstat; // These are dynamic status pointers. RxStat *rxsp; TxStat *txsp;#ifdef CYGPKG_NET cyg_interrupt interrupt; cyg_handle_t interrupt_handle;#endif // CYGPKG_NET unsigned char enaddr[ETHER_ADDR_LEN]; cyg_uint8 hardwired_esa; int txbusy; // Number of packets already queued int txnext; // Index to next hdr to use unsigned long txkey[CYGNUM_DEVS_ETH_ARM_EP93XX_TxNUM]; // Used to ack when packet sent unsigned long rxmode; // From here downwards, never access these directly, use // the uncachable pointers above: RxHdr _rxhdrs[CYGNUM_DEVS_ETH_ARM_EP93XX_RxNUM]; RxStat _rxstat[CYGNUM_DEVS_ETH_ARM_EP93XX_RxNUM]; TxHdr _txhdrs[CYGNUM_DEVS_ETH_ARM_EP93XX_TxNUM]; TxStat _txstat[CYGNUM_DEVS_ETH_ARM_EP93XX_TxNUM]; // The actual data buffers unsigned char _rxbufs[CYGNUM_DEVS_ETH_ARM_EP93XX_RxNUM] [CYGNUM_DEVS_ETH_ARM_EP93XX_BUFSIZE]; unsigned char _txbufs[CYGNUM_DEVS_ETH_ARM_EP93XX_TxNUM] [CYGNUM_DEVS_ETH_ARM_EP93XX_BUFSIZE];} _ep93xx_priv_data = {#ifdef CYGSEM_ARM_EP93XX_SET_ESA hardwired_esa: 1, enaddr: CYGDAT_ARM_EP93XX_ESA,#else hardwired_esa: 0,#endif};ETH_DRV_SC(ep93xx_sc, &_ep93xx_priv_data, // Driver specific data "eth0", // Name for this interface ep93xx_start, ep93xx_stop, ep93xx_control, ep93xx_can_send, ep93xx_send, ep93xx_recv, ep93xx_deliver, // "pseudoDSR" called from fast net thread ep93xx_poll, // poll function, encapsulates ISR and DSR ep93xx_int_vector);NETDEVTAB_ENTRY(ep93xx_netdev, "ep93xx", ep93xx_init, &ep93xx_sc);static void ep93xx_poll(struct eth_drv_sc *sc);#ifdef CYGPKG_NET// This ISR is called when the ethernet interrupt occursstatic intep93xx_isr(cyg_vector_t vector, cyg_addrword_t data, HAL_SavedRegisters *regs){// in case functionality moves into the ISR so that poll() uses it.#ifdef CYGPKG_NET cyg_drv_interrupt_mask(ETHERNET_INTERRUPT);#endif // CYGPKG_NET return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR); // Run the DSR}#endif // CYGPKG_NET// The deliver function (ex-DSR) handles the ethernet [logical] processingstatic voidep93xx_deliver(struct eth_drv_sc *sc){#ifdef CYGPKG_NET ep93xx_poll(sc); // Allow interrupts to happen again cyg_drv_interrupt_acknowledge(ETHERNET_INTERRUPT); cyg_drv_interrupt_unmask(ETHERNET_INTERRUPT);#endif // CYGPKG_NET}static intep93xx_int_vector(struct eth_drv_sc *sc){ return (ETHERNET_INTERRUPT);}#define ERR_PHY_OK (0)#define ERR_PHY_UNKNOWN (-1)#define ERR_PHY_BUSY (-2)#define ERR_PHY_TIMEOUT (-3)#define PHY_TIMEOUT 10000static intreadPHY(int phy, int reg, unsigned long *val){ unsigned long stat; int timeout;#ifdef ETHDEBUG //dprintf("readPHY()\n");#endif // ETHDEBUG timeout = PHY_TIMEOUT; do { if (--timeout == 0) { #ifdef ETHDEBUG dprintf("readPHY() Phy Busy\n"); #endif // ETHDEBUG return ERR_PHY_BUSY; } HAL_READ_UINT32(MAC_MiiStat, stat); } while (stat & MAC_MiiStat_Busy); HAL_WRITE_UINT32(MAC_MiiCmd, MiiCmdRead|(phy<<5)|reg); timeout = PHY_TIMEOUT; do { if (--timeout == 0) { #ifdef ETHDEBUG dprintf("readPHY() Phy timeout\n"); #endif // ETHDEBUG return ERR_PHY_TIMEOUT; } HAL_READ_UINT32(MAC_MiiStat, stat); } while (stat & MAC_MiiStat_Busy); HAL_READ_UINT32(MAC_MiiData, stat); *val = stat; return ERR_PHY_OK;}static voidwritePHY(int phy, int reg, unsigned long val){ unsigned long stat;#ifdef ETHDEBUG //dprintf("writePHY()\n");#endif // ETHDEBUG do { HAL_READ_UINT32(MAC_MiiStat, stat); } while (stat & MAC_MiiStat_Busy); HAL_WRITE_UINT32(MAC_MiiData, val); HAL_WRITE_UINT32(MAC_MiiCmd, MiiCmdWrite|(phy<<5)|reg); do { HAL_READ_UINT32(MAC_MiiStat, stat); } while (stat & MAC_MiiStat_Busy);}voiddoPHY(void){ unsigned long stat1, stat2, oldSelfCtlVal, testCtlVal; int indx, err; cyg_uint32 phys_found = 0; // bitset of good PHYs found cyg_uint32 phys_good = 0; // bitset of PHYs found with good link HAL_READ_UINT32(MAC_SelfCtl, oldSelfCtlVal); //Set MDC clock to be divided by 8, and disable PreambleSurpress //so that MAC can read/write PHY registers. HAL_WRITE_UINT32(MAC_SelfCtl, 0x0e00); // First scan for existant PHYs on the MII bus#ifdef ETHDEBUG dprintf("... Scan for PHY units\n");#endif // ETHDEBUG // PHY ID 0 is for broadcasting to all PHYs. Don't scan it. for (indx = 1; indx < 32; indx++) { err = readPHY(indx, PHY_ID_ONE, &stat1); if (err != ERR_PHY_OK) continue; if (stat1 == 0x0000FFFF) // Most likely, this device isn't there continue; err = readPHY(indx, PHY_ID_TWO, &stat2); if (err != ERR_PHY_OK) continue;#ifdef ETHDEBUG dprintf("PHY ID[%x] = %x/%x", indx, stat1, stat2);#endif // ETHDEBUG // Some of these bits latch and only reflect "the truth" on a 2nd reading. // So read and discard. readPHY(indx, PHY_CONTROL_REG, &stat2); readPHY(indx, PHY_STATUS_REG , &stat1); readPHY(indx, PHY_CONTROL_REG, &stat2); readPHY(indx, PHY_STATUS_REG, &stat1);#ifdef ETHDEBUG dprintf(", stat = %x, control = %x\n", stat1, stat2);#endif // ETHDEBUG phys_found |= (1<<indx); // Command the PHY to renegotiate the link and advertise! readPHY(indx, PHY_AUTONEG_ADVERT , &stat1); stat1 |= (PHY_AUTONEG_100BASETX_FDX + PHY_AUTONEG_100BASETX_HDX + PHY_AUTONEG_10BASET_FDX + PHY_AUTONEG_10BASET_HDX); writePHY(indx, PHY_AUTONEG_ADVERT , stat1); stat2 |= PHY_CONTROL_AUTONEG_EN | PHY_CONTROL_AUTONEG_RST; stat2 &=~PHY_CONTROL_POWERDOWN; writePHY(indx, PHY_CONTROL_REG, stat2 ); hal_delay_us( 1000 ); } for (indx = 1; indx < 32; indx++) { unsigned long i, j; if ( 0 == (phys_found & (1<<indx)) ) continue; i = 3000; // 3 seconds should be enough do { hal_delay_us( 1000 ); err = readPHY(indx, PHY_STATUS_REG, &stat1 ); if (err != ERR_PHY_OK || 0 == i-- ) break; } while ( 0 == (PHY_STATUS_AUTONEG_ACK & stat1) ); // Then autonegotiation is complete (or timed out) readPHY(indx, PHY_STATUS_REG, &stat1 ); if ( (PHY_STATUS_LINK_OK & stat1) ) { readPHY(indx, PHY_AUTONEG_ADVERT, &i ); readPHY(indx, PHY_AUTONEG_REMOTE, &j );#ifdef ETHDEBUG dprintf( "MII %d: capabilities are %04x, %04x; common %04x\n", indx, i, j, i & j );#endif // ETHDEBUG j &= i; if ( j & (PHY_AUTONEG_100BASETX_FDX + PHY_AUTONEG_100BASETX_HDX + PHY_AUTONEG_10BASET_FDX + PHY_AUTONEG_10BASET_HDX) ) { // Then there is commonality of capabilities, we are happy! phys_good |= (1<<indx); // OPTIONAL: having found one good interface, that's // enough: saves wasting time scanning all - when the // network device seems unable to use other PHYs. break; } if ( j & (PHY_AUTONEG_100BASETX_FDX + PHY_AUTONEG_10BASET_FDX)) { // If PHY is full duplex, set MAC to full duplex too. HAL_READ_UINT32(MAC_TestCtl, testCtlVal); testCtlVal |= MAC_TestCtl_FDX; HAL_WRITE_UINT32(MAC_TestCtl, testCtlVal); } } } // so there is at least one good link there if ( 0 == phys_good ) phys_good = phys_found & ~(phys_found-1);#ifdef ETHDEBUG dprintf( "After scan, phys_found %08x, phys_good %08x\n", phys_found, phys_good );#endif // ETHDEBUG // select the first one we find phys_good = phys_good & ~(phys_good-1); for (indx = 1; indx < 32; indx++) { if ( (1<<indx) & phys_found ) { if ( 0 == ((1<<indx) & phys_good) ) {#ifdef ETHDEBUG dprintf("Disabling PHY unit %d\n", indx );#endif // ETHDEBUG // Disable second PHY; Power down device writePHY(indx, PHY_CONTROL_REG, PHY_CONTROL_POWERDOWN ); } } } // restore the old vaule of SelfCtl register. HAL_WRITE_UINT32(MAC_SelfCtl, oldSelfCtlVal);}// ------------------------------------------------------------------------//// Reset the receiver and rebuild queues, etc.//static voidep93xx_RxReset(struct ep93xx_priv_data *cpd){ cyg_uint32 stat; cyg_uint32 phys; int i;#ifdef ETHDEBUG dprintf("ep93xx_RxReset()\n");#endif // ETHDEBUG // Reset Rx engine HAL_WRITE_UINT32(MAC_BMCtl, MAC_BMCtl_RxReset); do { HAL_READ_UINT32(MAC_BMCtl, stat); } while (stat & MAC_BMCtl_RxReset); HAL_WRITE_UINT32(MAC_RxCtl, 0); // Set up queues HAL_VIRT_TO_PHYS_ADDRESS( (cyg_uint32)cpd->rxhdrs, phys ); HAL_WRITE_UINT32(MAC_RxDBA, phys ); HAL_WRITE_UINT32(MAC_RxDBL, CYGNUM_DEVS_ETH_ARM_EP93XX_RxNUM * sizeof(RxHdr)); HAL_WRITE_UINT32(MAC_RxDCA, phys ); HAL_VIRT_TO_PHYS_ADDRESS( (cyg_uint32)cpd->rxstat, phys ); HAL_WRITE_UINT32(MAC_RxSBA, phys ); HAL_WRITE_UINT32(MAC_RxSBL, CYGNUM_DEVS_ETH_ARM_EP93XX_RxNUM * sizeof(RxStat)); HAL_WRITE_UINT32(MAC_RxSCA, phys ); HAL_WRITE_UINT32(MAC_RxDTH, 0x00040002); HAL_WRITE_UINT32(MAC_RxSTH, 0x00040002); HAL_WRITE_UINT32(MAC_RxBTH, 0x00800040); cpd->rxsp = cpd->rxstat; for (i = 0; i < CYGNUM_DEVS_ETH_ARM_EP93XX_RxNUM; i++) { HAL_VIRT_TO_PHYS_ADDRESS( (cyg_uint32)&cpd->_rxbufs[i][0], phys ); cpd->rxhdrs[i].bufaddr = (void *)phys; cpd->rxhdrs[i].bi = i; cpd->rxhdrs[i].len = CYGNUM_DEVS_ETH_ARM_EP93XX_BUFSIZE; } // Wait for Receiver to get started HAL_WRITE_UINT32(MAC_BMCtl, MAC_BMCtl_RxEnable); do { HAL_READ_UINT32(MAC_BMStat, stat); } while ((stat & MAC_BMStat_RxAct) == 0) ; HAL_WRITE_UINT32(MAC_RxDEQ, CYGNUM_DEVS_ETH_ARM_EP93XX_RxNUM); HAL_WRITE_UINT32(MAC_RxSEQ, CYGNUM_DEVS_ETH_ARM_EP93XX_RxNUM); // Configure and enable receiver HAL_WRITE_UINT32(MAC_MaxFL, (CYGNUM_DEVS_ETH_ARM_EP93XX_BUFSIZE<<16));}// ------------------------------------------------------------------------static voidep93xx_TxReset(struct ep93xx_priv_data *cpd){ cyg_uint32 stat; cyg_uint32 phys;#ifdef ETHDEBUG dprintf("ep93xx_TxReset()\n");#endif // ETHDEBUG // Clear all test options (was: set FD => ignore CSMA!) HAL_WRITE_UINT32(MAC_TestCtl, 0); HAL_VIRT_TO_PHYS_ADDRESS( (unsigned long)cpd->txhdrs, phys ); HAL_WRITE_UINT32(MAC_TxDBA, phys ); HAL_WRITE_UINT32(MAC_TxDBL, CYGNUM_DEVS_ETH_ARM_EP93XX_TxNUM * sizeof(TxHdr)); HAL_WRITE_UINT32(MAC_TxDCA, phys ); HAL_VIRT_TO_PHYS_ADDRESS( (unsigned long)cpd->txstat, phys ); HAL_WRITE_UINT32(MAC_TxSBA, phys ); HAL_WRITE_UINT32(MAC_TxSBL, CYGNUM_DEVS_ETH_ARM_EP93XX_TxNUM * sizeof(TxStat)); HAL_WRITE_UINT32(MAC_TxSCA, phys ); HAL_WRITE_UINT32(MAC_TxDTH, 0x00040002); HAL_WRITE_UINT32(MAC_TxSTH, 0x00040002); HAL_WRITE_UINT32(MAC_TxDTH, 0x00200010); cpd->txsp = cpd->txstat; // Configure and enable transmitter HAL_WRITE_UINT32(MAC_BMCtl, MAC_BMCtl_TxEnable); do { HAL_READ_UINT32(MAC_BMStat, stat);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -