//========================================================================== // // if_at91.c // // // //========================================================================== //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. // Copyright (C) 2006 Andrew Lunn // // 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., // 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. // // 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): Andrew Lunn, John Eigelaar, Juergen Lambrecht // Contributors: // Date: 2006-05-10 // Purpose: BSD compatible network driver // Description: HW network driver for AT91 EMAC block of AT91SAM uC's. // Limitations: Jumbo frames are not supported because of // AT91_EMAC_RBD_SR_LEN_MASK = 0xFFF // //####DESCRIPTIONEND#### // //========================================================================*/ #include #include #include #include #if defined(CYGPKG_REDBOOT) #include #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef CYGPKG_DEVS_ETH_PHY #include #endif #include #include #ifdef TLVFUN_DEVS_ETH_ARM_FPGA_SMI #include #endif // Set up the level of debug output #if CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL > 0 #define debug1_printf(args...) diag_printf(args) #else #define debug1_printf(args...) #endif #if CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL > 1 #define debug2_printf(args...) diag_printf(args) #else #define debug2_printf(args...) #endif //Driver interface callbacks #define _eth_drv_init(sc,mac) \ (sc->funs->eth_drv->init)(sc,(unsigned char *)mac) #define _eth_drv_tx_done(sc,key,status) \ (sc->funs->eth_drv->tx_done)(sc,key,status) #define _eth_drv_recv(sc,len) \ (sc->funs->eth_drv->recv)(sc,len) #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED static cyg_uint32 at91_eth_isr (cyg_vector_t vector, cyg_addrword_t data); #endif // -------------------------------------------------------------- // RedBoot configuration options for managing ESAs for us // Decide whether to have redboot config vars for it... #if defined(CYGSEM_REDBOOT_FLASH_CONFIG) && defined(CYGPKG_REDBOOT_NETWORKING) #include #include #ifdef CYGSEM_DEVS_ETH_ARM_AT91_REDBOOT_HOLDS_ESA_ETH0 RedBoot_config_option("Network hardware address [MAC] for eth0", eth0_esa_data, ALWAYS_ENABLED, true, CONFIG_ESA, 0); #endif #endif // CYGPKG_REDBOOT_NETWORKING && CYGSEM_REDBOOT_FLASH_CONFIG // and initialization code to read them // - independent of whether we are building RedBoot right now: //#ifdef CYGPKG_DEVS_ETH_ARM_AT91_REDBOOT_HOLDS_ESA //the application has to provide the flash config access function; if not, a //NOP will be executed, so also ok. #include #if defined (CYGPKG_IO_REDBOOT_VVT) #include #endif #ifndef CONFIG_ESA #define CONFIG_ESA (6) #endif /* #define CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA( mac_address, ok ) \ */ /* CYG_MACRO_START \ */ /* ok = CYGACC_CALL_IF_FLASH_CFG_OP( CYGNUM_CALL_IF_FLASH_CFG_GET, \ */ /* "eth0_esa_data", \ */ /* mac_address, \ */ /* CONFIG_ESA); \ */ /* CYG_MACRO_END */ /* struct cyg_fconfig info; */ /* info.key = "eth0_esa_data"; */ /* info.val = (void *)&enaddr[0]; */ /* info.type = CONFIG_ESA; */ /* info.offset = 0; */ /* esa_ok = CYGACC_CALL_IF_FLASH_CFG_OP2(CYGNUM_CALL_IF_FLASH_CFG_GET, &info); */ //#endif // CYGPKG_DEVS_ETH_ARM_AT91_REDBOOT_HOLDS_ESA //============================================================================ // Private Data structures #ifndef AT91_EMAC_RX_BUFF_SIZE #define AT91_EMAC_RX_BUFF_SIZE 128 #endif // Receive Buffer Descriptor typedef struct rbd_s { cyg_uint32 addr; cyg_uint32 sr; } rbd_t; // Receive Buffer typedef struct rb_s { cyg_uint8 rb[AT91_EMAC_RX_BUFF_SIZE]; } rb_t; // Transmit Buffer Descriptor typedef struct tbd_s { cyg_uint32 addr; cyg_uint32 sr; } tbd_t; // AT91 Ethernet private data typedef struct at91_eth_priv_s { cyg_uint32 intr_vector; char *esa_key; // RedBoot 'key' for device ESA cyg_uint8 *enaddr; cyg_uint32 base; // Base address of device #ifdef CYGPKG_DEVS_ETH_PHY eth_phy_access_t *phy; #endif rbd_t rbd[CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS]; rb_t rb[CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS] __attribute__ ((aligned (16))); tbd_t tbd[CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS]; unsigned long curr_tx_key; cyg_bool tx_busy; cyg_uint32 last_tbd_idx; cyg_uint32 curr_tbd_idx; cyg_uint32 curr_rbd_idx; #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED cyg_interrupt intr; cyg_handle_t intr_handle; cyg_uint32 isr; //to inform deliver() of calling IRQ #endif #ifndef CYGPKG_REDBOOT struct ether_drv_stats ifStats; #endif } at91_eth_priv_t; #ifdef CYGPKG_DEVS_ETH_PHY //============================================================================ // PHY access bits and pieces // static void at91_mdio_enable(void) { cyg_uint32 val; HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_NCR, val); val |= AT91_EMAC_NCR_MPE; /* enable management port */ HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_NCR, val); } static void at91_mdio_disable(void) { cyg_uint32 val; HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_NCR, val); val &= ~AT91_EMAC_NCR_MPE; /* disable management port */ HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_NCR, val); } // Write one of the PHY registers via the MII bus void at91_write_phy(int reg_addr, int phy_addr, unsigned short data) { cyg_uint32 val, cnt=0; CYG_ASSERTC(reg_addr >= 0 && reg_addr <= AT91_EMAC_MAN_REGA_MASK); CYG_ASSERTC(phy_addr >= 0 && phy_addr <= AT91_EMAC_MAN_PHY_MASK); val = (AT91_EMAC_MAN_SOF | AT91_EMAC_MAN_WR | AT91_EMAC_MAN_CODE | AT91_EMAC_MAN_PHYA(phy_addr) | AT91_EMAC_MAN_REGA(reg_addr) | AT91_EMAC_MAN_DATA(data)); HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_MAN, val); /* Wait until IDLE bit in Network Status register is cleared */ while (cnt < 1000000) { HAL_READ_UINT32((AT91_EMAC + AT91_EMAC_NSR), val); if (!(val & AT91_EMAC_NSR_IDLE)) break; } CYG_ASSERTC(cnt < 1000000); debug2_printf("at91_write_phy 0x%X 0x%X 0x%X\n", (unsigned int)reg_addr, (unsigned int)phy_addr, (unsigned int)data); } // Read one of the PHY registers via the MII bus bool at91_read_phy(int reg_addr, int phy_addr, unsigned short *data) { cyg_uint32 val; CYG_ASSERTC(reg_addr >= 0 && reg_addr <= AT91_EMAC_MAN_REGA_MASK); CYG_ASSERTC(phy_addr >= 0 && phy_addr <= AT91_EMAC_MAN_PHY_MASK); val = (AT91_EMAC_MAN_SOF | AT91_EMAC_MAN_RD | AT91_EMAC_MAN_CODE | AT91_EMAC_MAN_PHYA(phy_addr) | AT91_EMAC_MAN_REGA(reg_addr)); HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_MAN, val); /* Wait until IDLE bit in Network Status register is cleared */ do { HAL_READ_UINT32((AT91_EMAC + AT91_EMAC_NSR), val); }while(val & AT91_EMAC_NSR_IDLE); HAL_DELAY_US(50); HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_MAN, val); *data = val & AT91_EMAC_MAN_DATA_MASK; debug2_printf("at91_read_phy 0x%X 0x%X 0x%X\n", (unsigned int)reg_addr, (unsigned int)phy_addr, (unsigned int)(*data)); return (true); } // Enable the MDIO bit in MAC control register so that we can talk to // the PHY. Also set the clock divider so that MDC is less than 2.5MHz. static void at91_init_phy(void) { cyg_uint32 cfg; cyg_uint32 div; HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_NCFG, cfg); cfg &=~ AT91_EMAC_NCFG_CLK_MASK; div = (CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / 2500000); if (div < 8) { cfg |= AT91_EMAC_NCFG_CLK_HCLK_8; } else if (div < 16) { cfg |= AT91_EMAC_NCFG_CLK_HCLK_16; } else if (div < 32) { cfg |= AT91_EMAC_NCFG_CLK_HCLK_32; } else if (div < 64) { cfg |= AT91_EMAC_NCFG_CLK_HCLK_64; } else { CYG_FAIL("Unable to program MII clock"); } HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_NCFG, cfg); } /* The general PHY driver must know how to access the PHY. */ ETH_PHY_REG_LEVEL_ACCESS_FUNS(at91_phy, at91_init_phy, NULL, at91_write_phy, at91_read_phy); #endif // PHY access bits and pieces //====================================================================== // Receiver buffer handling // Initialize the receiver buffers and descriptors static void at91_rb_init(at91_eth_priv_t *priv) { int i; debug1_printf("RX buf base address = 0x%X\n", ((cyg_uint32)&priv->rb[0])); for (i = 0 ; i < CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS; i++) { priv->rbd[i].addr = ((cyg_uint32)&priv->rb[i]) & AT91_EMAC_RBD_ADDR_MASK; priv->rbd[i].sr = 0; } // Set the wrap bit on the last entry priv->rbd[CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS-1].addr |= AT91_EMAC_RBD_ADDR_WRAP; } //====================================================================== // Transmit buffer handling // Initialize the transmit buffer descriptors static void at91_tb_init(at91_eth_priv_t *priv) { int i; for (i = 0 ; i < CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS; i++) { priv->tbd[i].addr = 0; priv->tbd[i].sr = 0; /* AT91_EMAC_TBD_SR_USED; */ /* datasheet is wrong! 36.4.1.3 for SAM9260 vG and 37.4.1.3 for SAM7 vG point 2 */ } // Set the wrap bit on the last entry priv->tbd[CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS-1].sr |= AT91_EMAC_TBD_SR_WRAP; } //====================================================================== // Enable and Disable of the receiver and transmitter. static void at91_disable_rx(at91_eth_priv_t *priv) { cyg_uint32 ctl; HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl); ctl &= ~AT91_EMAC_NCR_RE; HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl); } static void at91_disable_tx(at91_eth_priv_t *priv) { cyg_uint32 ctl; HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl); ctl &= ~AT91_EMAC_NCR_TX; HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl); } static void at91_enable_rx(at91_eth_priv_t *priv) { cyg_uint32 ctl; HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl); ctl |= AT91_EMAC_NCR_RE; HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl); } static void at91_enable_tx(at91_eth_priv_t *priv) { cyg_uint32 ctl; HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl); ctl |= AT91_EMAC_NCR_TX; HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl); } static void at91_enable(at91_eth_priv_t *priv) { at91_enable_tx(priv); at91_enable_rx(priv); } static void at91_disable(at91_eth_priv_t *priv) { at91_disable_tx(priv); at91_disable_rx(priv); } static void at91_start_transmitter(at91_eth_priv_t *priv) { cyg_uint32 ctl; HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl); ctl |= AT91_EMAC_NCR_TSTART; HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl); } //====================================================================== // Initialization code // Configure the pins so that the EMAC has control of them. This // assumes the MII is used, not the RMII static void at91_cfg_pins(void) { HAL_ARM_AT91_PIO_CFG(AT91_EMAC_EREFCK); #ifdef CYGPKG_DEVS_ETH_PHY HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ECRS); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ECOL); #endif HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERXDV); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX0); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX1); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX2); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX3); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERXER); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERXCK); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETXEN); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX0); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX1); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX2); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX3); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETXER); #ifdef CYGPKG_DEVS_ETH_PHY HAL_ARM_AT91_PIO_CFG(AT91_EMAC_EMDC); HAL_ARM_AT91_PIO_CFG(AT91_EMAC_EMDIO); #endif } // Set a specific address match to a given address. Packets received which // match this address will be passed on. static void at91_set_mac(at91_eth_priv_t * priv, cyg_uint8 * enaddr, int sa) { cyg_uint32 hi, lo; CYG_ASSERTC(sa > 0 && sa < 5); sa--; lo = ((enaddr[3] << 24) | (enaddr[2] << 16) | (enaddr[1] << 8) | (enaddr[0])); hi = ((enaddr[5] << 8) | (enaddr[4])); HAL_WRITE_UINT32(priv->base + AT91_EMAC_SA1L + (8*sa), lo); HAL_WRITE_UINT32(priv->base + AT91_EMAC_SA1H + (8*sa), hi); } static void at91_clear_stats(at91_eth_priv_t *priv) { cyg_uint32 ctl; HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl); ctl |= AT91_EMAC_NCR_CSR; HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl); } // Enable and Disable of the receiver and transmitter. // Initialize the interface. This configures the interface ready for use. // Interrupts are grabbed etc. This means the start function has // little to do except enable the receiver static bool at91_eth_init(struct cyg_netdevtab_entry *tab) { struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance; at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private; bool esa_ok = false; unsigned char enaddr[6] = { CYGPKG_DEVS_ETH_ARM_AT91_MACADDR}; unsigned char enzero[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; #ifdef CYGPKG_DEVS_ETH_PHY unsigned short phy_state = 0; #endif cyg_uint32 ncfg = 0; debug1_printf("\nAT91_ETH: Initialising @ %x\n",priv->base); priv->tx_busy = false; priv->curr_tbd_idx = 0; priv->curr_rbd_idx = 0; #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED priv->isr = 0; #endif // Enable the clock to the EMAC - TODO: is it needed to enable PIO clk?? HAL_WRITE_UINT32(AT91_PMC + AT91_PMC_PCER, AT91_PMC_PCER_EMAC); /* HAL_WRITE_UINT32(AT91_PMC + AT91_PMC_PCER, AT91_PMC_PCER_PIOB); */ /* HAL_WRITE_UINT32(AT91_PMC + AT91_PMC_PCER, AT91_PMC_PCER_PIOA); */ //at91_disable(priv); at91_cfg_pins(); /* Enable IO Clock */ HAL_WRITE_UINT32(priv->base+AT91_EMAC_USRIO,AT91_EMAC_USRIO_CLKEN); /* Disable all the interrupts for the moment */ /* The Start function actually enables all that we need */ //HAL_WRITE_UINT32(priv->base + AT91_EMAC_IDR, 0x3FFF); // If we are building an interrupt enabled version, install the // interrupt handler #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED debug1_printf("AT91_ETH: Installing Interrupts on IRQ %d\n", priv->intr_vector); cyg_drv_interrupt_create(priv->intr_vector, 4, (cyg_addrword_t)sc, at91_eth_isr, eth_drv_dsr, &priv->intr_handle, &priv->intr); /* default is level sensitive; */ cyg_drv_interrupt_attach(priv->intr_handle); cyg_drv_interrupt_unmask(priv->intr_vector); #endif /* #ifdef CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA */ // Get MAC address from RedBoot configuration variables /* CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA(&enaddr[0], esa_ok); */ esa_ok = flash_get_config("eth0_esa_data", &enaddr[0], CONFIG_ESA); // If this call fails myMacAddr is unchanged and MAC address from // CDL is used /* #endif */ if (!esa_ok) { // Can't figure out ESA debug1_printf("AT91_ETH - Warning! ESA unknown\n"); } debug1_printf("AT91_ETH: init %02x:%02x:%02x:%02x:%02x:%02x\n", enaddr[0],enaddr[1],enaddr[2], enaddr[3],enaddr[4],enaddr[5]); // Give the EMAC its address at91_set_mac(priv, enaddr, 1); at91_set_mac(priv, enzero, 2); at91_set_mac(priv, enzero, 3); at91_set_mac(priv, enzero, 4); // Setup the receiver buffers and descriptors at91_rb_init(priv); // And tell the EMAC where the first receive buffer descriptor is HAL_WRITE_UINT32(priv->base + AT91_EMAC_RBQP, (cyg_uint32)priv->rbd); // Setup the transmit descriptors at91_tb_init(priv); // And tell the EMAC where the first transmit buffer descriptor is HAL_WRITE_UINT32(priv->base + AT91_EMAC_TBQP, (cyg_uint32)priv->tbd); #ifdef CYGPKG_DEVS_ETH_PHY // Setup the PHY CYG_ASSERTC(priv->phy); at91_mdio_enable(); if (!_eth_phy_init(priv->phy)) { at91_mdio_disable(); debug1_printf("_eth_phy_init failed\n"); return (false); } else {debug1_printf("_eth_phy_init succeeded\n");} // Get the current mode and print it phy_state = _eth_phy_state(priv->phy); //at91_mdio_disable(); JL: why disable?? #endif HAL_READ_UINT32(priv->base + AT91_EMAC_NCFG,ncfg); #ifdef CYGPKG_DEVS_ETH_PHY if ((phy_state & ETH_PHY_STAT_LINK) != 0) { if (((phy_state & ETH_PHY_STAT_100MB) != 0)) { debug1_printf("AT91_ETH: 100Mbyte/s"); #endif ncfg |= AT91_EMAC_NCFG_SPD_100Mbps; #ifdef CYGPKG_DEVS_ETH_PHY } else { debug1_printf("AT91_ETH: 10Mbyte/s"); ncfg &= ~(AT91_EMAC_NCFG_SPD_100Mbps); } if((phy_state & ETH_PHY_STAT_FDX)) { debug1_printf(" Full Duplex\n"); #endif ncfg |= AT91_EMAC_NCFG_FD; #ifdef CYGPKG_DEVS_ETH_PHY } else { debug1_printf(" Half Duplex\n"); ncfg &= ~(AT91_EMAC_NCFG_FD); } } else { debug1_printf("AT91_ETH: No Link\n"); } #endif #if CYGINT_IO_ETH_MULTICAST /* Enable multicast */ ncfg |= AT91_EMAC_NCFG_MTI; #endif //Setup the network configuration ncfg |= (AT91_EMAC_NCFG_RLCE); HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCFG,ncfg); // Clear the Statistics counters; at91_clear_stats(priv); /* Clear the status registers */ HAL_READ_UINT32(priv->base + AT91_EMAC_ISR,ncfg); HAL_READ_UINT32(priv->base + AT91_EMAC_RSR,ncfg); HAL_WRITE_UINT32(priv->base + AT91_EMAC_RSR,ncfg); HAL_READ_UINT32(priv->base + AT91_EMAC_TSR,ncfg); HAL_WRITE_UINT32(priv->base + AT91_EMAC_TSR,ncfg); // Initialize the upper layer driver _eth_drv_init(sc,enaddr); return (true); } // This function is called to stop the interface. static void at91_eth_stop(struct eth_drv_sc *sc) { at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private; #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED cyg_uint32 bits; bits = (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_RBNA | AT91_EMAC_ISR_ROVR | AT91_EMAC_ISR_HRESP | AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM); HAL_WRITE_UINT32(priv->base + AT91_EMAC_IDR, bits); #endif at91_disable(priv); } // This function is called to "start up" the interface. It may be called // multiple times, even when the hardware is already running. static void at91_eth_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags) { at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private; #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED cyg_uint32 bits; // Enable the interrupts we are interested in /* The following errors only cause the internal counter/pointer to be reset if also bit 6 TXERR is set: AT91_EMAC_ISR_TOVR | AT91_EMAC_ISR_TUND |AT91_EMAC_ISR_RTRY | The datasheet is not clear about this... */ bits = (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_RBNA | AT91_EMAC_ISR_ROVR | AT91_EMAC_ISR_HRESP | AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM); HAL_WRITE_UINT32(priv->base + AT91_EMAC_IER, bits); #endif // Enable the receiver and transmitter at91_enable(priv); } // This function is called for low level "control" operations static int at91_eth_control(struct eth_drv_sc *sc, unsigned long key, void *data, int length) { at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private; switch (key) { #ifdef TLVFUN_DEVS_ETH_ARM_FPGA_SMI //the defines below are only valid if CYGPKG_NET, so not for RedBoot // to use statistics, look at intel/i82559/../if_i82559.c case ETH_DRV_GET_IF_STATS_UD: case ETH_DRV_GET_IF_STATS: { struct ether_drv_stats *p = (struct ether_drv_stats*)data; cyg_uint16 linkStatus; ReadMII(ETH0_SMI_ADDR, 0x00, &linkStatus); if (linkStatus & PhyStatus_Resolved) { priv->ifStats.resolved = 3; if (linkStatus & PhyStatus_FullDuplex) priv->ifStats.duplex = 3; else priv->ifStats.duplex = 2; if (linkStatus & PhyStatus_100Mb) priv->ifStats.speed = 100000000; else priv->ifStats.speed = 10000000; } else { priv->ifStats.resolved = 2; priv->ifStats.duplex = 1; } if (linkStatus & PhyStatus_LinkUp) priv->ifStats.operational = 3; else priv->ifStats.operational = 2; *p = priv->ifStats; return 0; } #else #ifndef CYGPKG_REDBOOT /* Default we assume the link is up. * Eg: DVA board * Needed for DHCP */ case ETH_DRV_GET_IF_STATS_UD: case ETH_DRV_GET_IF_STATS: { struct ether_drv_stats *p = (struct ether_drv_stats*)data; priv->ifStats.resolved = 3; priv->ifStats.duplex = 3; /* Full duplex */ priv->ifStats.speed = 100000000; /* 100 Mbit */ priv->ifStats.operational = 3; *p = priv->ifStats; return 0; } #endif #endif case ETH_DRV_SET_MAC_ADDRESS: { if(length >= ETHER_ADDR_LEN) { at91_eth_stop(sc); cyg_uint8 * enaddr = (cyg_uint8 *)data; debug1_printf("AT91_ETH: set %02x:%02x:%02x:%02x:%02x:%02x\n", enaddr[0],enaddr[1],enaddr[2], enaddr[3],enaddr[4],enaddr[5]); at91_set_mac((at91_eth_priv_t *)sc->driver_private,enaddr,1); at91_eth_start(sc,enaddr,0); return 0; } return 1; } #if CYGINT_IO_ETH_MULTICAST case ETH_DRV_SET_MC_ALL: { HAL_WRITE_UINT32(priv->base+AT91_EMAC_HRB,0xFFFFFFFF); HAL_WRITE_UINT32(priv->base+AT91_EMAC_HRT,0xFFFFFFFF); return 0; } case ETH_DRV_SET_MC_LIST: { HAL_WRITE_UINT32(priv->base+AT91_EMAC_HRB,0xFFFFFFFF); HAL_WRITE_UINT32(priv->base+AT91_EMAC_HRT,0xFFFFFFFF); return 0; /* struct eth_drv_mc_list* mcl = (struct eth_drv_mc_list*) data; for (i=0; ilen; i++) { //mcl->addrs[0][0]==0x01; mcl->addrs[0][1]==0x00; mcl->addrs[0][2]==0x5E; stdims_write(eth, FPGA_MC_OFFSET+4*i, ((mcl->addrs[i][4]<<8)|(mcl->addrs[i][5]))); stdims_write(eth, FPGA_MC_OFFSET+2+4*i, ((mcl->addrs[i][2]<<8)|(mcl->addrs[i][3]))); } for (i=mcl->len; idriver_private; if(priv->tx_busy) { can_send = 0; } else { can_send = 1; } return (can_send); } // This routine is called to send data to the hardware static void at91_eth_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total_len, unsigned long key) { at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private; int i; cyg_uint32 sr; #ifdef SRAM1_ORIGIN /* define it in plf_io.h if present */ cyg_uint32 total_bytes = 0; /* position in SRAM1 */ //TODO: this code abuses that the transmit buffer only contains 1 packet //because can_send() returns false as long as the only-one buffer is not cleared #endif priv->tx_busy = true; /* for can_send() */ priv->last_tbd_idx = priv->curr_tbd_idx; for(i = 0;itbd[priv->curr_tbd_idx].addr = SRAM1_ORIGIN; sr = (total_bytes & AT91_EMAC_TBD_SR_LEN_MASK); //this clears also used bit sr |= AT91_EMAC_TBD_SR_EOF; #else priv->tbd[priv->curr_tbd_idx].addr = sg_list[i].buf; sr = (sg_list[i].len & AT91_EMAC_TBD_SR_LEN_MASK); // Set the End Of Frame bit in the last descriptor if(i == (sg_len-1)) { sr |= AT91_EMAC_TBD_SR_EOF; } #endif /* TODO: add this again to use a circular buffer for transmit */ /* if(priv->curr_tbd_idx < (CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS-1)) */ /* { */ /* priv->tbd[priv->curr_tbd_idx].sr = sr; */ /* priv->curr_tbd_idx++; */ /* } */ /* else */ /* { */ priv->tbd[priv->curr_tbd_idx].sr = (sr | AT91_EMAC_TBD_SR_WRAP); /* priv->curr_tbd_idx = 0; */ /* } */ #ifndef SRAM1_ORIGIN } /* When NOT using SRAM, use sg_list buffers */ #endif // Store away the key for when the transmit has completed // and we need to tell the stack which transmit has completed. priv->curr_tx_key = key; at91_start_transmitter(priv); } /* datasheet 36.4.1.3 for SAM9260 vG and 37.4.1.3 for SAM7 vG point 2: set AT91_EMAC_TBD_SR_USED=0 */ static void at91_reset_tbd(at91_eth_priv_t *priv, bool b_reset_tbd_idx) { /* cyg_uint32 tbd_idx; */ priv->tbd[0].sr = AT91_EMAC_TBD_SR_WRAP; /* while(priv->curr_tbd_idx != priv->last_tbd_idx) */ /* { */ /* if(priv->last_tbd_idx == (CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS-1)) */ /* { */ /* priv->tbd[priv->last_tbd_idx].sr = AT91_EMAC_TBD_SR_WRAP; */ /* priv->last_tbd_idx = 0; */ /* } */ /* else */ /* { */ /* priv->tbd[priv->last_tbd_idx].sr = 0; */ /* priv->last_tbd_idx++; */ /* } */ /* } */ /* HAL_READ_UINT32(priv->base+AT91_EMAC_TBQP,tbd_idx); //synchronize */ /* * priv->last_tbd_idx = priv->curr_tbd_idx = * */ /* tbd_idx -= (cyg_uint32)priv->tbd; */ /* debug2_printf("tbd_idx: %X\n", priv->curr_tbd_idx); */ /* if (b_reset_tbd_idx) */ /* { */ /* priv->curr_tbd_idx = 0; //reset, because EMAC also resets its queue */ /* } //pointer to the TBQP. No need to reset last_tbd_idx: is each time reinitialized */ /* assert(tbd_idx==priv->curr_tbd_idx); */ } //====================================================================== #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED static cyg_uint32 at91_eth_isr (cyg_vector_t vector, cyg_addrword_t data) { struct eth_drv_sc *sc = (struct eth_drv_sc *)data; at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private; cyg_uint32 ret; cyg_uint32 isr; /* Get the interrupt status */ HAL_READ_UINT32(priv->base+AT91_EMAC_ISR,isr); priv->isr |= isr; //could be several irqs before the dsr is run ret = CYG_ISR_HANDLED; /* The following errors only cause the internal counter/pointer to be reset if also bit 6 TXERR is set: AT91_EMAC_ISR_TOVR | AT91_EMAC_ISR_TUND |AT91_EMAC_ISR_RTRY | The datasheet is not clear about this... */ if(isr & (AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM)) { ret = CYG_ISR_CALL_DSR; /* debug2_printf("TX IRQ\n"); */ } if(isr & (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_RBNA | AT91_EMAC_ISR_ROVR | AT91_EMAC_ISR_HRESP)) { ret = CYG_ISR_CALL_DSR; /* debug2_printf("RX IRQ\n"); */ } cyg_interrupt_acknowledge(vector); return(ret); } #endif static void at91_eth_deliver(struct eth_drv_sc *sc) { at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private; #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED cyg_uint32 isr; #endif cyg_uint32 tsr=0; cyg_uint32 rsr=0; cyg_uint32 ctr; cyg_uint32 cnt; cyg_uint32 idx; bool b_reset_tbd_idx = false; #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED cyg_interrupt_disable(); isr = priv->isr; priv->isr = 0; cyg_interrupt_enable(); debug2_printf("local IRQ %X\n", isr); #else debug2_printf("AT91_ETH in\n"); #endif #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED if (isr & (AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM)) { #endif /* Get the Transmit Status and clear it */ HAL_READ_UINT32(priv->base+AT91_EMAC_TSR,tsr); HAL_WRITE_UINT32(priv->base+AT91_EMAC_TSR,tsr); #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED } if(isr & (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_RBNA | AT91_EMAC_ISR_ROVR | AT91_EMAC_ISR_HRESP)) { #endif /* Get the Receive Status and clear it */ HAL_READ_UINT32(priv->base+AT91_EMAC_RSR,rsr); HAL_WRITE_UINT32(priv->base+AT91_EMAC_RSR,rsr); #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED } #endif /* Service the TX buffers after IRQ */ if (tsr&AT91_EMAC_TSR_OVR) //0 { debug2_printf("AT91_ETH: Tx UBR\n"); } if (tsr&AT91_EMAC_TSR_COL) //1 { debug2_printf("AT91_ETH: Tx COL\n"); } if (tsr&AT91_EMAC_TSR_RLE) //2 { debug1_printf("AT91_ETH: Tx RLE\n"); #ifndef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED b_reset_tbd_idx = true; #endif } if ((tsr&AT91_EMAC_TSR_TXIDLE) == 0) //3 { debug2_printf("AT91_ETH: Tx IDLE\n"); } if (tsr&AT91_EMAC_TSR_BNQ) //4 { debug1_printf("AT91_ETH: Tx BEX\n"); #ifndef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED b_reset_tbd_idx = true; #endif } if (tsr&AT91_EMAC_TSR_UND) //6 { debug1_printf("AT91_ETH: Tx UND\n"); #ifndef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED b_reset_tbd_idx = true; #endif } /* Check that the last transmission is completed */ if (tsr&AT91_EMAC_TSR_COMP) //5 { debug2_printf("AT91_ETH: Tx COMP\n"); } #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED b_reset_tbd_idx = (isr & AT91_EMAC_ISR_TBRE); #endif /* After each transmit, it is best to immediately reset tbd */ /* If there were errors, tbd_idx must be reset also */ //Do it always: if already done, curr==last; synchronization stays #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED if (isr & (AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM)) #else if ((tsr&AT91_EMAC_TSR_COMP) || (b_reset_tbd_idx == true)) //5 #endif { at91_reset_tbd(priv, b_reset_tbd_idx); priv->tx_busy = false; _eth_drv_tx_done(sc,priv->curr_tx_key,0); } /* Check for receive errors, and fix them if needed/possible. */ if (rsr&AT91_EMAC_RSR_OVR) { debug1_printf("AT91_ETH: Rx OVR\n"); } if (rsr&AT91_EMAC_RSR_BNA) { /* clear all used bits; something went wrong, or we're not fast enough.. */ debug1_printf("AT91_ETH: Rx BNA\n"); for (idx=0; idxrbd[idx].addr &= ~(AT91_EMAC_RBD_ADDR_OWNER_SW); } return; } /* Service the RX buffers when we get something after IRQ */ #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED if (isr & (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_ROVR | AT91_EMAC_ISR_HRESP)) #else if (rsr& (AT91_EMAC_RSR_REC|AT91_EMAC_RSR_OVR)) #endif { /* at OVR, only the last buffer is recover by the EMAC; SW should recover the other ones */ #if 0 cyg_uint32 rbd_idx; /* Enable this if you don't trust the hardware */ #endif debug2_printf("AT91_ETH: Rx COMP\n"); /* Do this all until we find the first EMAC Buffer */ while (priv->rbd[priv->curr_rbd_idx].addr & AT91_EMAC_RBD_ADDR_OWNER_SW) { //Firstly walk through to either the first buffer that belongs // to the controller or the first SOF while ((priv->rbd[priv->curr_rbd_idx].addr & AT91_EMAC_RBD_ADDR_OWNER_SW) && !(priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_SOF)) { priv->rbd[priv->curr_rbd_idx].addr &= ~(AT91_EMAC_RBD_ADDR_OWNER_SW); priv->curr_rbd_idx++; if (priv->curr_rbd_idx >= CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS) { priv->curr_rbd_idx = 0; } #if 0 /* check if we're not accessing same buffer as EMAC HW */ HAL_READ_UINT32(priv->base+AT91_EMAC_RBQP,rbd_idx); rbd_idx &= ~0x1; /* counts per 2 so last bit=0 */ rbd_idx -= (cyg_uint32)priv->rbd; /* we want the counter, not the address */ if (rbd_idx == priv->curr_rbd_idx) { debug1_printf("AT91_ETH: sw catched hw!\n"); return; //how to break; out of 2 loops? } #endif } /* Check that we did find a SOF (and not an OWNER_SW=0) */ if ((priv->rbd[priv->curr_rbd_idx].addr & AT91_EMAC_RBD_ADDR_OWNER_SW) && (priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_SOF)) { ctr = 0; idx = priv->curr_rbd_idx; /* Look for an EOF as long as the SW owns the buffer */ while (priv->rbd[idx].addr & AT91_EMAC_RBD_ADDR_OWNER_SW) /* for (ctr=0;ctrbase+AT91_EMAC_RBQP,rbd_idx); rbd_idx &= ~0x1; /* counts per 2 so last bit=0 */ rbd_idx -= (cyg_uint32)priv->rbd; /* we want the counter, not the address */ if (rbd_idx == idx) { debug1_printf("AT91_ETH: SW catched HW!\n"); return; //how to break; out of 2 loops? } #endif /* check if not again a SOF */ if (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_SOF) { /* recover buffers */ while (priv->curr_rbd_idx != idx) { priv->rbd[priv->curr_rbd_idx].addr &= ~(AT91_EMAC_RBD_ADDR_OWNER_SW); priv->curr_rbd_idx++; if (priv->curr_rbd_idx >= CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS) { priv->curr_rbd_idx = 0; } } } /* look for EOF */ if (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_EOF) { /* only for last buffer the length is valid */ cnt = (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_LEN_MASK); /* The recv function will adjust the current buffer idx after the buffer has been cleared */ if (cnt) { _eth_drv_recv(sc,cnt);} /* TODO: this function can fail, and not call local recv, so not clear the OWNER_SW bit */ else { /* recover buffers */ debug1_printf("AT91_ETH: Rx 0 B?\n"); while (priv->curr_rbd_idx != idx) { priv->rbd[priv->curr_rbd_idx].addr &= ~(AT91_EMAC_RBD_ADDR_OWNER_SW); priv->curr_rbd_idx++; if (priv->curr_rbd_idx >= CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS) { priv->curr_rbd_idx = 0; } } } debug2_printf("AT91_ETH: Rx data\n"); break; } ctr++; idx = (ctr+priv->curr_rbd_idx)%CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS; } } } } debug2_printf("AT91_ETH out\n"); } static void at91_eth_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len) { at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private; int i; cyg_uint32 bytes_in_buffer; /* # not yet copied bytes in EMAC buffer */ cyg_uint32 bytes_in_list = 0; /* position in current sg_list */ cyg_uint32 bytes_needed_list = 0; /* # bytes still free in sg_list */ cyg_uint32 buffer_pos = 0; /* position in current EMAC buffer */ cyg_uint8 * sg_buf; cyg_uint32 total_bytes = 0; /* total no. of packet bytes already copied */ for(i = 0;irbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_EOF) { /* This 128B buffer contains the End Of the Frame. */ /* AT91_EMAC_RBD_SR_LEN_MASK contains Length of frame including FCS (if selected) */ bytes_in_buffer = (priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_LEN_MASK) - total_bytes; /* Not '- buffer_pos' because that is already contained in total_bytes. */ } else { /* AT91_EMAC_RX_BUFF_SIZE = 128B */ bytes_in_buffer = AT91_EMAC_RX_BUFF_SIZE - buffer_pos; } sg_buf = (cyg_uint8 *)(sg_list[i].buf); if(bytes_needed_list < bytes_in_buffer) { if(sg_buf != NULL) memcpy(&sg_buf[bytes_in_list], &priv->rb[priv->curr_rbd_idx].rb[buffer_pos], bytes_needed_list); bytes_in_list += bytes_needed_list; buffer_pos += bytes_needed_list; total_bytes += bytes_needed_list; } else /* all data from EMAC buffer can be copied */ { /* also here for last bytes of packet: sg_len[last].len matches */ if(sg_buf != NULL) memcpy(&sg_buf[bytes_in_list], &priv->rb[priv->curr_rbd_idx].rb[buffer_pos], bytes_in_buffer); bytes_in_list += bytes_in_buffer; total_bytes += bytes_in_buffer; /* Step our buffer on one */ priv->rbd[priv->curr_rbd_idx].addr &= ~(AT91_EMAC_RBD_ADDR_OWNER_SW); priv->curr_rbd_idx++; if(priv->curr_rbd_idx >= CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS) { priv->curr_rbd_idx = 0; } buffer_pos = 0; } } } } // routine called to handle ethernet controller in polled mode static void at91_eth_poll(struct eth_drv_sc *sc) { /* Service the buffers */ at91_eth_deliver(sc); } static int at91_eth_int_vector(struct eth_drv_sc *sc) { return(CYGNUM_HAL_INTERRUPT_EMAC); } at91_eth_priv_t at91_priv_data = { .intr_vector = CYGNUM_HAL_INTERRUPT_EMAC, .base = AT91_EMAC #ifdef CYGPKG_DEVS_ETH_PHY , .phy = &at91_phy #endif }; ETH_DRV_SC(at91_sc, &at91_priv_data, // Driver specific data "eth0", // Name for this interface at91_eth_start, at91_eth_stop, at91_eth_control, at91_eth_can_send, at91_eth_send, at91_eth_recv, at91_eth_deliver, at91_eth_poll, at91_eth_int_vector); NETDEVTAB_ENTRY(at91_netdev, "at91", at91_eth_init, &at91_sc); // EOF if_at91.c