diff options
author | Thomas Doerfler <Thomas.Doerfler@embedded-brains.de> | 2009-10-15 14:05:34 +0000 |
---|---|---|
committer | Thomas Doerfler <Thomas.Doerfler@embedded-brains.de> | 2009-10-15 14:05:34 +0000 |
commit | 045821e17447a0a948d530b5f20b0cb73fd3d2de (patch) | |
tree | cf58e57f6e42edf2134531a13ef5bac583d6a90e /c/src/lib/libbsp/m68k/genmcf548x | |
parent | add MC_DMA support to MCF548x (diff) | |
download | rtems-045821e17447a0a948d530b5f20b0cb73fd3d2de.tar.bz2 |
add network support, various corrections
Diffstat (limited to 'c/src/lib/libbsp/m68k/genmcf548x')
-rw-r--r-- | c/src/lib/libbsp/m68k/genmcf548x/ChangeLog | 6 | ||||
-rw-r--r-- | c/src/lib/libbsp/m68k/genmcf548x/Makefile.am | 4 | ||||
-rw-r--r-- | c/src/lib/libbsp/m68k/genmcf548x/clock/clock.c | 23 | ||||
-rw-r--r-- | c/src/lib/libbsp/m68k/genmcf548x/console/console.c | 12 | ||||
-rw-r--r-- | c/src/lib/libbsp/m68k/genmcf548x/include/bsp.h | 17 | ||||
-rw-r--r-- | c/src/lib/libbsp/m68k/genmcf548x/network/network.c | 1869 | ||||
-rw-r--r-- | c/src/lib/libbsp/m68k/genmcf548x/startup/init548x.c | 4 |
7 files changed, 1898 insertions, 37 deletions
diff --git a/c/src/lib/libbsp/m68k/genmcf548x/ChangeLog b/c/src/lib/libbsp/m68k/genmcf548x/ChangeLog index 10e9adf730..f756488aa4 100644 --- a/c/src/lib/libbsp/m68k/genmcf548x/ChangeLog +++ b/c/src/lib/libbsp/m68k/genmcf548x/ChangeLog @@ -1,3 +1,9 @@ +2009-10-15 Thomas Doerfler <Thomas.Doerfler@embedded-brains.de> + + * Makefile.am, include/bsp.h, network/network.c: add network support + * console/console.c, clock/clock.c, startup/init548x.c: + various corrections + 2009-10-15 Ralf Corsépius <ralf.corsepius@rtems.org> * make/custom/genmcf548x.cfg: New (relocated from /make/custom). diff --git a/c/src/lib/libbsp/m68k/genmcf548x/Makefile.am b/c/src/lib/libbsp/m68k/genmcf548x/Makefile.am index d43461b25c..7b5b83d6e6 100644 --- a/c/src/lib/libbsp/m68k/genmcf548x/Makefile.am +++ b/c/src/lib/libbsp/m68k/genmcf548x/Makefile.am @@ -53,7 +53,9 @@ network_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) endif libbsp_a_LIBADD = ../../../libcpu/@RTEMS_CPU@/shared/cache.rel \ - ../../../libcpu/@RTEMS_CPU@/shared/misc.rel + ../../../libcpu/@RTEMS_CPU@/shared/misc.rel \ + ../../../libcpu/@RTEMS_CPU@/mcf548x/mcdma.rel + if HAS_NETWORKING libbsp_a_LIBADD += network.rel endif diff --git a/c/src/lib/libbsp/m68k/genmcf548x/clock/clock.c b/c/src/lib/libbsp/m68k/genmcf548x/clock/clock.c index 153a50fac9..67ba779d98 100644 --- a/c/src/lib/libbsp/m68k/genmcf548x/clock/clock.c +++ b/c/src/lib/libbsp/m68k/genmcf548x/clock/clock.c @@ -87,18 +87,17 @@ * We need to have 1 interrupt every 10,000 microseconds * XLB clock 100 MHz / MCF548X_SLT_SLTCNT0 = XLB clock/100 */ -#define Clock_driver_support_initialize_hardware() \ - do { \ - int level; \ - MCF548X_INTC_ICR54 = MCF548X_INTC_ICRn_IL(SLT0_IRQ_LEVEL) | \ - MCF548X_INTC_ICRn_IP(SLT0_IRQ_PRIORITY); \ - rtems_interrupt_disable( level ); \ - MCF548X_INTC_IMRH &= ~(MCF548X_INTC_IMRH_INT_MASK54 | \ - MCF548X_INTC_IMRL_MASKALL); \ - rtems_interrupt_enable( level ); \ - MCF548X_SLT_SLTCNT0 = get_CPU_clock_speed()/100; \ - MCF548X_SLT_SCR0 |= (MCF548X_SLT_SCR_TEN | MCF548X_SLT_SCR_RUN | MCF548X_SLT_SCR_IEN); \ - } while (0) +#define Clock_driver_support_initialize_hardware() \ + do { \ + int level; \ + MCF548X_INTC_ICR54 = MCF548X_INTC_ICRn_IL(SLT0_IRQ_LEVEL) | \ + MCF548X_INTC_ICRn_IP(SLT0_IRQ_PRIORITY); \ + rtems_interrupt_disable( level ); \ + MCF548X_INTC_IMRH &= ~(MCF548X_INTC_IMRH_INT_MASK54); \ + rtems_interrupt_enable( level ); \ + MCF548X_SLT_SLTCNT0 = get_CPU_clock_speed()/100; \ + MCF548X_SLT_SCR0 |= (MCF548X_SLT_SCR_TEN | MCF548X_SLT_SCR_RUN | MCF548X_SLT_SCR_IEN); \ + } while (0) #include "../../../shared/clockdrv_shell.h" diff --git a/c/src/lib/libbsp/m68k/genmcf548x/console/console.c b/c/src/lib/libbsp/m68k/genmcf548x/console/console.c index 600641eab2..6c12079f0b 100644 --- a/c/src/lib/libbsp/m68k/genmcf548x/console/console.c +++ b/c/src/lib/libbsp/m68k/genmcf548x/console/console.c @@ -485,29 +485,25 @@ IntUartInitialize(void) case 0: MCF548X_INTC_ICR35 = MCF548X_INTC_ICRn_IL(PSC0_IRQ_LEVEL) | MCF548X_INTC_ICRn_IP(PSC0_IRQ_PRIORITY); - MCF548X_INTC_IMRH &= ~(MCF548X_INTC_IMRH_INT_MASK35 | - MCF548X_INTC_IMRL_MASKALL); + MCF548X_INTC_IMRH &= ~(MCF548X_INTC_IMRH_INT_MASK35); break; case 1: MCF548X_INTC_ICR34 = MCF548X_INTC_ICRn_IL(PSC1_IRQ_LEVEL) | MCF548X_INTC_ICRn_IP(PSC1_IRQ_PRIORITY); - MCF548X_INTC_IMRH &= ~(MCF548X_INTC_IMRH_INT_MASK34 | - MCF548X_INTC_IMRL_MASKALL); + MCF548X_INTC_IMRH &= ~(MCF548X_INTC_IMRH_INT_MASK34); break; case 2: MCF548X_INTC_ICR33 = MCF548X_INTC_ICRn_IL(PSC2_IRQ_LEVEL) | MCF548X_INTC_ICRn_IP(PSC2_IRQ_PRIORITY); - MCF548X_INTC_IMRH &= ~(MCF548X_INTC_IMRH_INT_MASK33 | - MCF548X_INTC_IMRL_MASKALL); + MCF548X_INTC_IMRH &= ~(MCF548X_INTC_IMRH_INT_MASK33); break; case 3: MCF548X_INTC_ICR32 = MCF548X_INTC_ICRn_IL(PSC3_IRQ_LEVEL) | MCF548X_INTC_ICRn_IP(PSC3_IRQ_PRIORITY); - MCF548X_INTC_IMRH &= ~(MCF548X_INTC_IMRH_INT_MASK32 | - MCF548X_INTC_IMRL_MASKALL); + MCF548X_INTC_IMRH &= ~(MCF548X_INTC_IMRH_INT_MASK32); break; } rtems_interrupt_enable(level); diff --git a/c/src/lib/libbsp/m68k/genmcf548x/include/bsp.h b/c/src/lib/libbsp/m68k/genmcf548x/include/bsp.h index c42c8025db..362d2dd209 100644 --- a/c/src/lib/libbsp/m68k/genmcf548x/include/bsp.h +++ b/c/src/lib/libbsp/m68k/genmcf548x/include/bsp.h @@ -91,10 +91,6 @@ m68k_isr_entry set_vector( * Interrupt assignments * Highest-priority listed first */ -#define FEC_IRQ_LEVEL 4 -#define FEC_IRQ_RX_PRIORITY 7 -#define FEC_IRQ_TX_PRIORITY 6 - #define SLT0_IRQ_LEVEL 4 #define SLT0_IRQ_PRIORITY 0 @@ -107,6 +103,19 @@ m68k_isr_entry set_vector( #define PSC3_IRQ_LEVEL 3 #define PSC3_IRQ_PRIORITY 4 +#define FEC_IRQ_LEVEL 2 +#define FEC_IRQ_PRIORITY 3 + +/* + * Network driver configuration + */ +struct rtems_bsdnet_ifconfig; +extern int rtems_mcf548x_fec_driver_attach_detach(struct rtems_bsdnet_ifconfig *config,int attaching); +#define RTEMS_BSP_NETWORK_DRIVER_ATTACH rtems_mcf548x_fec_driver_attach_detach + +#define RTEMS_BSP_NETWORK_DRIVER_NAME "fec1" +#define RTEMS_BSP_NETWORK_DRIVER_NAME2 "fec2" + #ifdef __cplusplus } #endif diff --git a/c/src/lib/libbsp/m68k/genmcf548x/network/network.c b/c/src/lib/libbsp/m68k/genmcf548x/network/network.c index ad26e2ca78..eb038e8008 100644 --- a/c/src/lib/libbsp/m68k/genmcf548x/network/network.c +++ b/c/src/lib/libbsp/m68k/genmcf548x/network/network.c @@ -1,20 +1,1869 @@ - -#include <bsp.h> -#include <stdio.h> -#include <errno.h> -#include <stdarg.h> -#include <string.h> +/*===============================================================*\ +| Project: RTEMS generic MCF548X BSP | ++-----------------------------------------------------------------+ +| Partially based on the code references which are named below. | +| Adaptions, modifications, enhancements and any recent parts of | +| the code are: | +| Copyright (c) 2009 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | ++-----------------------------------------------------------------+ +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| | +| http://www.rtems.com/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| this file contains the networking driver | +\*===============================================================*/ +/* + * RTEMS/TCPIP driver for MCF548X FEC Ethernet + * + * Modified for Motorola MPC5200 by Thomas Doerfler, <Thomas.Doerfler@imd-systems.de> + * COPYRIGHT (c) 2003, IMD + * + * Modified for Motorola IceCube (mgt5100) by Peter Rasmussen <prasmus@ipr-engineering.de> + * COPYRIGHT (c) 2003, IPR Engineering + * + * Parts of code are also under property of Driver Information Systems and based + * on Motorola Proprietary Information. + * COPYRIGHT (c) 2002 MOTOROLA INC. + * + * Modified for Motorola MCF548X by Thomas Doerfler, <Thomas.Doerfler@imd-systems.de> + * COPYRIGHT (c) 2009, IMD + * + */ #include <rtems.h> #include <rtems/error.h> #include <rtems/rtems_bsdnet.h> - +#include <stdio.h> #include <sys/param.h> #include <sys/mbuf.h> #include <sys/socket.h> #include <sys/sockio.h> - -#include <net/ethernet.h> #include <net/if.h> - #include <netinet/in.h> #include <netinet/if_ether.h> +#include <net/if_var.h> + +#include <bsp.h> +#include <mcf548x/mcf548x.h> +#include <errno.h> + +/* freescale-api-specifics... */ +#include <mcf548x/MCD_dma.h> +#include <mcf548x/mcdma_glue.h> + +#define ETH_PROMISCOUS_MODE 1 /* FIXME: remove me */ + +/* + * Number of interfaces supported by this driver + */ +#define NIFACES 2 + +/* + * buffer descriptor handling + */ + +#define SET_BD_STATUS(bd, stat) { \ + (bd)->statCtrl = stat; \ +} +#define SET_BD_LENGTH(bd, len) { \ + (bd)->length = len; \ +} +#define SET_BD_BUFFER(bd, buf) { \ + (bd)->dataPointer= (uint32_t)(buf); \ +} +#define GET_BD_STATUS(bd) ((bd)->statCtrl) +#define GET_BD_LENGTH(bd) ((bd)->length) +#define GET_BD_BUFFER(bd) ((void *)((bd)->dataPointer)) + +#define DMA_BD_RX_NUM 32 /* Number of receive buffer descriptors */ +#define DMA_BD_TX_NUM 32 /* Number of transmit buffer descriptors */ + +/* + * internal SRAM + * Layout: + * - RxBD channel 0 + * - TxBD channel 0 + * - RxBD channel 1 + * - TxBD channel 1 + * - DMA task memory + */ +extern char _SysSramBase[]; +#define SRAM_RXBD_BASE(base,chan) (((MCD_bufDescFec*)(base)) \ + +((chan) \ + *(DMA_BD_RX_NUM+DMA_BD_TX_NUM))) + +#define SRAM_TXBD_BASE(base,chan) (((MCD_bufDescFec*)(base)) \ + +((chan) \ + *(DMA_BD_RX_NUM+DMA_BD_TX_NUM) \ + +DMA_BD_RX_NUM)) + +#define SRAM_DMA_BASE(base) ((void *)SRAM_RXBD_BASE(base,NIFACES+1)) + + +#define ETH_DEBUG + +/* + * Default number of buffer descriptors set aside for this driver. + * The number of transmit buffer descriptors has to be quite large + * since a single frame often uses four or more buffer descriptors. + */ +#define RX_BUF_COUNT DMA_BD_RX_NUM +#define TX_BUF_COUNT DMA_BD_TX_NUM +#define TX_BD_PER_BUF 1 + +#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255") + +#define MCF548X_FEC0_IRQ_VECTOR (39+64) +#define MCF548X_FEC1_IRQ_VECTOR (38+64) + +#define MCF548X_FEC_IRQ_VECTOR(chan) (MCF548X_FEC0_IRQ_VECTOR \ + +(chan)*(MCF548X_FEC1_IRQ_VECTOR \ + -MCF548X_FEC0_IRQ_VECTOR)) + +#define MCF548X_FEC_VECTOR2CHAN(vector) (((int)(vector)-MCF548X_FEC0_IRQ_VECTOR) \ + /(MCF548X_FEC1_IRQ_VECTOR \ + -MCF548X_FEC0_IRQ_VECTOR)) + +#define FEC_RECV_TASK_NO 4 +#define FEC_XMIT_TASK_NO 5 + +#define MCDMA_FEC_RX_CHAN(chan) (0 + NIFACES*(chan)) +#define MCDMA_FEC_TX_CHAN(chan) (1 + NIFACES*(chan)) + +#define MCF548X_FEC0_RX_INITIATOR (16) +#define MCF548X_FEC1_RX_INITIATOR (30) +#define MCF548X_FEC_RX_INITIATOR(chan) (MCF548X_FEC0_RX_INITIATOR \ + +(chan)*(MCF548X_FEC1_RX_INITIATOR \ + -MCF548X_FEC0_RX_INITIATOR)) +#define MCF548X_FEC0_TX_INITIATOR (17) +#define MCF548X_FEC1_TX_INITIATOR (31) +#define MCF548X_FEC_TX_INITIATOR(chan) (MCF548X_FEC0_TX_INITIATOR \ + +(chan)*(MCF548X_FEC1_TX_INITIATOR \ + -MCF548X_FEC0_TX_INITIATOR)) + +/* + * RTEMS event used by interrupt handler to signal daemons. + * This must *not* be the same event used by the TCP/IP task synchronization. + */ +#define INTERRUPT_EVENT RTEMS_EVENT_1 +#define FATAL_INT_EVENT RTEMS_EVENT_3 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +/* BD and parameters are stored in SRAM(refer to sdma.h) */ +#define MCF548X_FEC_BD_BASE ETH_BD_BASE + +/* RBD bits definitions */ +#define MCF548X_FEC_RBD_EMPTY 0x8000 /* Buffer is empty */ +#define MCF548X_FEC_RBD_WRAP 0x2000 /* Last BD in ring */ +#define MCF548X_FEC_RBD_INT 0x1000 /* Interrupt */ +#define MCF548X_FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */ +#define MCF548X_FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */ +#define MCF548X_FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */ +#define MCF548X_FEC_RBD_MC 0x0040 /* The received frame is multicast frame */ +#define MCF548X_FEC_RBD_LG 0x0020 /* Frame length violation */ +#define MCF548X_FEC_RBD_NO 0x0010 /* Nonoctet align frame */ +#define MCF548X_FEC_RBD_SH 0x0008 /* Short frame, FEC does not support SH and this bit is always cleared */ +#define MCF548X_FEC_RBD_CR 0x0004 /* CRC error */ +#define MCF548X_FEC_RBD_OV 0x0002 /* Receive FIFO overrun */ +#define MCF548X_FEC_RBD_TR 0x0001 /* The receive frame is truncated */ +#define MCF548X_FEC_RBD_ERR (MCF548X_FEC_RBD_LG | \ + MCF548X_FEC_RBD_NO | \ + MCF548X_FEC_RBD_CR | \ + MCF548X_FEC_RBD_OV | \ + MCF548X_FEC_RBD_TR) + +/* TBD bits definitions */ +#define MCF548X_FEC_TBD_READY 0x8000 /* Buffer is ready */ +#define MCF548X_FEC_TBD_WRAP 0x2000 /* Last BD in ring */ +#define MCF548X_FEC_TBD_INT 0x1000 /* Interrupt */ +#define MCF548X_FEC_TBD_LAST 0x0800 /* Buffer is last in frame */ +#define MCF548X_FEC_TBD_TC 0x0400 /* Transmit the CRC */ +#define MCF548X_FEC_TBD_ABC 0x0200 /* Append bad CRC */ + +#define FEC_INTR_MASK_USED \ +(MCF548X_FEC_EIMR_LC | MCF548X_FEC_EIMR_RL | \ + MCF548X_FEC_EIMR_XFUN | MCF548X_FEC_EIMR_XFERR | MCF548X_FEC_EIMR_RFERR) + +/* + * Device data + */ +struct mcf548x_enet_struct { + struct arpcom arpcom; + struct mbuf **rxMbuf; + struct mbuf **txMbuf; + int chan; + int acceptBroadcast; + int rxBdCount; + int txBdCount; + int txBdHead; + int txBdTail; + int txBdActiveCount; + MCD_bufDescFec *rxBd; + MCD_bufDescFec *txBd; + int rxDmaChan; /* dma task */ + int txDmaChan; /* dma task */ + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + unsigned long rxInterrupts; + unsigned long rxNotLast; + unsigned long rxGiant; + unsigned long rxNonOctet; + unsigned long rxBadCRC; + unsigned long rxOverrun; + unsigned long rxCollision; + + unsigned long txInterrupts; + unsigned long txDeferred; + unsigned long txLateCollision; + unsigned long txUnderrun; + unsigned long txMisaligned; + unsigned long rxNotFirst; + unsigned long txRetryLimit; + }; + +static struct mcf548x_enet_struct enet_driver[NIFACES]; + +extern int taskTable; +static void mcf548x_fec_restart(struct mcf548x_enet_struct *sc); + + + +/* + * Function: mcf548x_fec_rx_bd_init + * + * Description: Initialize the receive buffer descriptor ring. + * + * Returns: void + * + * Notes: Space for the buffers of rx BDs is allocated by the rx deamon + * + */ +static void mcf548x_fec_rx_bd_init(struct mcf548x_enet_struct *sc) { + int rxBdIndex; + struct mbuf *m; + struct ifnet *ifp = &sc->arpcom.ac_if; + + /* + * Fill RX buffer descriptor ring. + */ + for( rxBdIndex = 0; rxBdIndex < sc->rxBdCount; rxBdIndex++ ) { + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rtems_cache_invalidate_multiple_data_lines(mtod(m,const void *), + ETHER_MAX_LEN); + SET_BD_BUFFER(sc->rxBd+rxBdIndex,mtod(m, void *)); + SET_BD_LENGTH(sc->rxBd+rxBdIndex,ETHER_MAX_LEN); + SET_BD_STATUS(sc->rxBd+rxBdIndex, + MCF548X_FEC_RBD_EMPTY + | MCF548X_FEC_RBD_INT + | ((rxBdIndex == sc->rxBdCount-1) + ? MCF548X_FEC_RBD_WRAP + : 0)); + } +} + +/* + * Function: mcf548x_fec_rx_bd_cleanup + * + * Description: put all mbufs pending in rx BDs back to buffer pool + * + * Returns: void + * + */ +static void mcf548x_fec_rx_bd_cleanup(struct mcf548x_enet_struct *sc) { + int rxBdIndex; + struct mbuf *m,*n; + + /* + * Drain RX buffer descriptor ring. + */ + for( rxBdIndex = 0; rxBdIndex < sc->rxBdCount; rxBdIndex++ ) { + n = sc->rxMbuf[rxBdIndex]; + while (n != NULL) { + m = n; + MFREE(m,n); + } + } +} + +/* + * Function: MCF548X_eth_addr_filter_set + * + * Description: Set individual address filter for unicast address and + * set physical address registers. + * + * Returns: void + * + * Notes: + * + */ +static void mcf548x_eth_addr_filter_set(struct mcf548x_enet_struct *sc) { + unsigned char *mac; + unsigned char currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + unsigned long crc = 0xffffffff; /* initial value */ + int chan = sc->chan; + + /* + * Get the mac address of ethernet controller + */ + mac = (unsigned char *)(&sc->arpcom.ac_enaddr); + + /* + * The algorithm used is the following: + * we loop on each of the six bytes of the provided address, + * and we compute the CRC by left-shifting the previous + * value by one position, so that each bit in the current + * byte of the address may contribute the calculation. If + * the latter and the MSB in the CRC are different, then + * the CRC value so computed is also ex-ored with the + * "polynomium generator". The current byte of the address + * is also shifted right by one bit at each iteration. + * This is because the CRC generatore in hardware is implemented + * as a shift-register with as many ex-ores as the radixes + * in the polynomium. This suggests that we represent the + * polynomiumm itself as a 32-bit constant. + */ + for(byte = 0; byte < 6; byte++) + { + + currByte = mac[byte]; + + for(bit = 0; bit < 8; bit++) + { + + if((currByte & 0x01) ^ (crc & 0x01)) + { + + crc >>= 1; + crc = crc ^ 0xedb88320; + + } + else + { + + crc >>= 1; + + } + + currByte >>= 1; + + } + + } + + crc = crc >> 26; + + /* + * Set individual hash table register + */ + if(crc >= 32) + { + + MCF548X_FEC_IAUR(chan) = (1 << (crc - 32)); + MCF548X_FEC_IALR(chan) = 0; + + } + else + { + + MCF548X_FEC_IAUR(chan) = 0; + MCF548X_FEC_IALR(chan) = (1 << crc); + + } + + /* + * Set physical address + */ + MCF548X_FEC_PALR(chan) = ((mac[0] << 24) + + (mac[1] << 16) + + (mac[2] << 8) + + mac[3]); + MCF548X_FEC_PAUR(chan) = ((mac[4] << 24) + + (mac[5] << 16)) + 0x8808; + + } + + +/* + * Function: mcf548x_eth_mii_read + * + * Description: Read a media independent interface (MII) register on an + * 18-wire ethernet tranceiver (PHY). Please see your PHY + * documentation for the register map. + * + * Returns: 32-bit register value + * + * Notes: + * + */ +int mcf548x_eth_mii_read(struct mcf548x_enet_struct *sc, unsigned char phyAddr, unsigned char regAddr, unsigned short * retVal) + { + int timeout = 0xffff; + int chan = sc->chan; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + MCF548X_FEC_MMFR(chan) = (MCF548X_FEC_MMFR_ST_01 | + MCF548X_FEC_MMFR_OP_READ | + MCF548X_FEC_MMFR_TA_10 | + MCF548X_FEC_MMFR_PA(phyAddr) | + MCF548X_FEC_MMFR_RA(regAddr)); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(MCF548X_FEC_EIR(chan) & MCF548X_FEC_EIR_MII))); + + if(timeout == 0) + { + +#ifdef ETH_DEBUG + printf ("Read MDIO failed..." "\r\n"); +#endif + + return false; + + } + + /* + * clear mii interrupt bit + */ + MCF548X_FEC_EIR(chan) = MCF548X_FEC_EIR_MII; + + /* + * it's now safe to read the PHY's register + */ + *retVal = (unsigned short) MCF548X_FEC_MMFR(chan); + + return true; + + } + +/* + * Function: mcf548x_eth_mii_write + * + * Description: Write a media independent interface (MII) register on an + * 18-wire ethernet tranceiver (PHY). Please see your PHY + * documentation for the register map. + * + * Returns: Success (boolean) + * + * Notes: + * + */ +static int mcf548x_eth_mii_write(struct mcf548x_enet_struct *sc, unsigned char phyAddr, unsigned char regAddr, unsigned short data) + { + int chan = sc->chan; + int timeout = 0xffff; + + MCF548X_FEC_MMFR(chan) = (MCF548X_FEC_MMFR_ST_01 | + MCF548X_FEC_MMFR_OP_WRITE | + MCF548X_FEC_MMFR_TA_10 | + MCF548X_FEC_MMFR_PA(phyAddr) | + MCF548X_FEC_MMFR_RA(regAddr) | + MCF548X_FEC_MMFR_DATA(data)); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(MCF548X_FEC_EIR(chan) & MCF548X_FEC_EIR_MII))); + + if(timeout == 0) + { + +#ifdef ETH_DEBUG + printf ("Write MDIO failed..." "\r\n"); +#endif + + return false; + + } + + /* + * clear MII interrupt bit + */ + MCF548X_FEC_EIR(chan) = MCF548X_FEC_EIR_MII; + + return true; + + } + + +/* + * Function: mcf548x_fec_reset + * + * Description: Reset a running ethernet driver including the hardware + * FIFOs and the FEC. + * + * Returns: Success (boolean) + * + * Notes: + * + */ +static int mcf548x_fec_reset(struct mcf548x_enet_struct *sc) { + volatile int delay; + int chan = sc->chan; + /* + * Clear FIFO status registers + */ + MCF548X_FEC_FECRFSR(chan) = ~0; + MCF548X_FEC_FECTFSR(chan) = ~0; + + /* + * reset the FIFOs + */ + MCF548X_FEC_FRST(chan) = 0x03000000; + + for (delay = 0;delay < 16*4;delay++) {}; + + MCF548X_FEC_FRST(chan) = 0x01000000; + + /* + * Issue a reset command to the FEC chip + */ + MCF548X_FEC_ECR(chan) |= MCF548X_FEC_ECR_RESET; + + /* + * wait at least 16 clock cycles + */ + for (delay = 0;delay < 16*4;delay++) {}; + + return true; +} + + +/* + * Function: mcf548x_fec_off + * + * Description: Stop the FEC and disable the ethernet SmartComm tasks. + * This function "turns off" the driver. + * + * Returns: void + * + * Notes: + * + */ +void mcf548x_fec_off(struct mcf548x_enet_struct *sc) + { + int counter = 0xffff; + int chan = sc->chan; + + +#if defined(ETH_DEBUG) + unsigned short phyStatus, i; + unsigned char phyAddr = 0; + + for(i = 0; i < 9; i++) + { + + mcf548x_eth_mii_read(sc, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x" "\r\n", i, phyStatus); + + } + + for(i = 16; i < 21; i++) + { + + mcf548x_eth_mii_read(sc, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x" "\r\n", i, phyStatus); + + } +#endif /* ETH_DEBUG */ + + /* + * block FEC chip interrupts + */ + MCF548X_FEC_EIMR(chan) = 0; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + MCF548X_FEC_TCR(chan) |= MCF548X_FEC_TCR_GTS; + + /* + * wait for graceful stop to register + * FIXME: add rtems_task_wake_after here, if it takes to long + */ + while((counter--) && (!( MCF548X_FEC_EIR(chan) & MCF548X_FEC_EIR_GRA))); + + /* + * Disable the SmartDMA transmit and receive tasks. + */ + MCD_killDma( sc->rxDmaChan ); + MCD_killDma( sc->txDmaChan ); + /* + * Disable transmit / receive interrupts + */ + mcdma_glue_irq_disable(sc->txDmaChan); + mcdma_glue_irq_disable(sc->rxDmaChan); + + /* + * Disable the Ethernet Controller + */ + MCF548X_FEC_ECR(chan) &= ~(MCF548X_FEC_ECR_ETHER_EN); + + /* + * cleanup all buffers + */ + mcf548x_fec_rx_bd_cleanup(sc); + + } + +/* + * MCF548X FEC interrupt handler + */ +void mcf548x_fec_irq_handler(rtems_vector_number vector) +{ + struct mcf548x_enet_struct *sc; + volatile uint32_t ievent; + int chan; + + sc = &(enet_driver[MCF548X_FEC_VECTOR2CHAN(vector)]); + chan = sc->chan; + ievent = MCF548X_FEC_EIR(chan); + + MCF548X_FEC_EIR(chan) = ievent; + /* + * check errors, update statistics + */ + if (ievent & MCF548X_FEC_EIR_LC) { + sc->txLateCollision++; + } + if (ievent & MCF548X_FEC_EIR_RL) { + sc->txRetryLimit++; + } + if (ievent & MCF548X_FEC_EIR_XFUN) { + sc->txUnderrun++; + } + if (ievent & MCF548X_FEC_EIR_XFERR) { + sc->txUnderrun++; + } + if (ievent & MCF548X_FEC_EIR_RFERR) { + sc->rxOverrun++; + } + /* + * fatal error ocurred? + */ + if (ievent & (MCF548X_FEC_EIR_RFERR | MCF548X_FEC_EIR_XFERR)) { + MCF548X_FEC_EIMR(chan) &=~(MCF548X_FEC_EIMR_RFERR | MCF548X_FEC_EIMR_XFERR); + rtems_event_send(sc->rxDaemonTid, FATAL_INT_EVENT); + } +} + +/* + * MCF548X DMA ethernet interrupt handler + */ +void mcf548x_mcdma_rx_irq_handler(void * param) +{ + struct mcf548x_enet_struct *sc = (struct mcf548x_enet_struct *)param; + /* Frame received? */ + if(MCDMA_GET_PENDING(sc->rxDmaChan)) { + MCDMA_CLR_PENDING(sc->rxDmaChan); + + mcdma_glue_irq_disable(sc->rxDmaChan);/*Disable receive ints*/ + sc->rxInterrupts++; /* Rx int has occurred */ + rtems_event_send(sc->rxDaemonTid, INTERRUPT_EVENT); + } +} + +/* + * MCF548X DMA ethernet interrupt handler + */ +void mcf548x_mcdma_tx_irq_handler(void * param) +{ + struct mcf548x_enet_struct *sc = (struct mcf548x_enet_struct *)param; + + /* Buffer transmitted or transmitter error? */ + if(MCDMA_GET_PENDING(sc->txDmaChan)) { + + MCDMA_CLR_PENDING(sc->txDmaChan); + + mcdma_glue_irq_disable(sc->txDmaChan);/*Disable tx ints*/ + + sc->txInterrupts++; /* Tx int has occurred */ + + rtems_event_send(sc->txDaemonTid, INTERRUPT_EVENT); + } +} + + + + + + /* + * Function: mcf548x_fec_retire_tbd + * + * Description: Soak up buffer descriptors that have been sent. + * + * Returns: void + * + * Notes: + * + */ +static void mcf548x_fec_retire_tbd(struct mcf548x_enet_struct *sc, + bool force) +{ + struct mbuf *n; + /* + * Clear already transmitted BDs first. Will not work calling same + * from fecExceptionHandler(TFINT). + */ + + while ((sc->txBdActiveCount > 0) && + (force || + ((MCF548X_FEC_TBD_READY & GET_BD_STATUS(sc->txBd+sc->txBdTail)) + == 0x0))) { + if (sc->txMbuf[sc->txBdTail] != NULL) { + /* + * NOTE: txMbuf can be NULL, if mbuf has been split into different BDs + */ + MFREE (sc->txMbuf[sc->txBdTail],n); + sc->txMbuf[sc->txBdTail] = NULL; + } + sc->txBdActiveCount--; + if(++sc->txBdTail >= sc->txBdCount) { + sc->txBdTail = 0; + } + } +} + +#if 0 + /* + * Function: mcf548x_fec_tx_bd_requeue + * + * Description: put buffers back to interface output queue + * + * Returns: void + * + * Notes: + * + */ +static void mcf548x_fec_tx_bd_requeue(struct mcf548x_enet_struct *sc) +{ + /* + * Clear already transmitted BDs first. Will not work calling same + * from fecExceptionHandler(TFINT). + */ + + while (sc->txBdActiveCount > 0) { + if (sc->txMbuf[sc->txBdHead] != NULL) { + /* + * NOTE: txMbuf can be NULL, if mbuf has been split into different BDs + */ + IF_PREPEND(&(sc->arpcom.ac_if.if_snd),sc->txMbuf[sc->txBdHead]); + sc->txMbuf[sc->txBdHead] = NULL; + } + sc->txBdActiveCount--; + if(--sc->txBdHead < 0) { + sc->txBdHead = sc->txBdCount-1; + } + } +} +#endif + +static void mcf548x_fec_sendpacket(struct ifnet *ifp,struct mbuf *m) { + struct mcf548x_enet_struct *sc = ifp->if_softc; + struct mbuf *l = NULL; + int nAdded; + uint32_t status; + rtems_event_set events; + MCD_bufDescFec *thisBd; + MCD_bufDescFec *firstBd = NULL; + void *data_ptr; + size_t data_len; + + /* + * Free up buffer descriptors + */ + mcf548x_fec_retire_tbd(sc,false); + + /* + * Set up the transmit buffer descriptors. + * No need to pad out short packets since the + * hardware takes care of that automatically. + * No need to copy the packet to a contiguous buffer + * since the hardware is capable of scatter/gather DMA. + */ + nAdded = 0; + + for(;;) { + + /* + * Wait for buffer descriptor to become available. + */ + if((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + + /* + * Clear old events + */ + MCDMA_CLR_PENDING(sc->txDmaChan); + /* + * Wait for buffer descriptor to become available. + * Note that the buffer descriptors are checked + * *before* * entering the wait loop -- this catches + * the possibility that a buffer descriptor became + * available between the `if' above, and the clearing + * of the event register. + * This is to catch the case where the transmitter + * stops in the middle of a frame -- and only the + * last buffer descriptor in a frame can generate + * an interrupt. + */ + mcf548x_fec_retire_tbd(sc,false); + + while((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + mcdma_glue_irq_enable(sc->txDmaChan); + rtems_bsdnet_event_receive(INTERRUPT_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, &events); + mcf548x_fec_retire_tbd(sc,false); + } + } + + if(m->m_len == 0) { + /* + * Just toss empty mbufs + */ + struct mbuf *n; + MFREE(m, n); + m = n; + if(l != NULL) { + l->m_next = m; + } + } + else { + /* + * Flush the buffer for this descriptor + */ + rtems_cache_flush_multiple_data_lines((const void *)mtod(m, void *), + m->m_len); + /* + * Fill in the buffer descriptor, + * set "end of frame" bit in status, + * if last mbuf in chain + */ + thisBd = sc->txBd + sc->txBdHead; + /* + * FIXME: do not send interrupt after every frame + * doing this every quarter of BDs is much more efficent + */ + status = (((m->m_next == NULL) + ? MCF548X_FEC_TBD_LAST | MCF548X_FEC_TBD_INT + : 0) + | ((sc->txBdHead == sc->txBdCount-1) + ? MCF548X_FEC_TBD_WRAP + :0 )); + /* + * Don't set the READY flag till the + * whole packet has been readied. + */ + if (firstBd != NULL) { + status |= MCF548X_FEC_TBD_READY; + } + else { + firstBd = thisBd; + } + + data_ptr = mtod(m, void *); + data_len = m->m_len; + sc->txMbuf[sc->txBdHead] = m; + /* go to next part in chain */ + l = m; + m = m->m_next; + + SET_BD_BUFFER(thisBd, data_ptr); + SET_BD_LENGTH(thisBd, data_len); + SET_BD_STATUS(thisBd, status); + + nAdded++; + if(++(sc->txBdHead) == sc->txBdCount) { + sc->txBdHead = 0; + } + } + /* + * Set the transmit buffer status. + * Break out of the loop if this mbuf is the last in the frame. + */ + if(m == NULL) { + if(nAdded) { + SET_BD_STATUS(firstBd, + GET_BD_STATUS(firstBd) | MCF548X_FEC_TBD_READY); + MCD_continDma(sc->txDmaChan); + sc->txBdActiveCount += nAdded; + } + break; + } + } /* end of for(;;) */ +} + + +/* + * Driver transmit daemon + */ +void mcf548x_fec_txDaemon(void *arg) + { + struct mcf548x_enet_struct *sc = (struct mcf548x_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for(;;) { + /* + * Wait for packet + */ + mcdma_glue_irq_enable(sc->txDmaChan); + rtems_bsdnet_event_receive(START_TRANSMIT_EVENT|INTERRUPT_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &events); + + /* + * Send packets till queue is empty + */ + for(;;) + { + + /* + * Get the next mbuf chain to transmit. + */ + IF_DEQUEUE(&ifp->if_snd, m); + + if (!m) + break; + + mcf548x_fec_sendpacket(ifp, m); + + } + + ifp->if_flags &= ~IFF_OACTIVE; + + } + + } + + +/* + * reader task + */ +static void mcf548x_fec_rxDaemon(void *arg){ + struct mcf548x_enet_struct *sc = (struct mcf548x_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + struct ether_header *eh; + int rxBdIndex; + uint32_t status; + size_t size; + rtems_event_set events; + size_t len = 1; + MCD_bufDescFec *bd; + void *dptr; + + /* + * Input packet handling loop + */ + rxBdIndex = 0; + + for (;;) { + /* + * Clear old events + */ + MCDMA_CLR_PENDING(sc->rxDmaChan); + /* + * Get the first BD pointer and its length. + */ + bd = sc->rxBd + rxBdIndex; + status = GET_BD_STATUS( bd ); + len = GET_BD_LENGTH( bd ); + + /* + * Loop through BDs until we find an empty one. This indicates that + * the DMA is still using it. + */ + while( !(status & MCF548X_FEC_RBD_EMPTY) ) { + + /* + * Remember the data pointer from this transfer. + */ + dptr = GET_BD_BUFFER(bd); + m = sc->rxMbuf[rxBdIndex]; + m->m_len = m->m_pkthdr.len = (len + - sizeof(uint32_t) + - sizeof(struct ether_header)); + eh = mtod(m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + ether_input(ifp, eh, m); + + /* + * Done w/ the BD. Clean it. + */ + sc->rxMbuf[rxBdIndex] = NULL; + + /* + * Add a new buffer to the ring. + */ + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + size = ETHER_MAX_LEN; + + sc->rxMbuf[rxBdIndex] = m; + rtems_cache_invalidate_multiple_data_lines(mtod(m,const void *), + size); + + SET_BD_BUFFER(bd,mtod(m, void *)); + SET_BD_LENGTH(bd,size); + SET_BD_STATUS(bd, + MCF548X_FEC_RBD_EMPTY + |MCF548X_FEC_RBD_INT + |((rxBdIndex == sc->rxBdCount-1) + ? MCF548X_FEC_RBD_WRAP + : 0) + ); + + /* + * advance to next BD + */ + if (++rxBdIndex >= sc->rxBdCount) { + rxBdIndex = 0; + } + /* + * Get next BD pointer and its length. + */ + bd = sc->rxBd + rxBdIndex; + status = GET_BD_STATUS( bd ); + len = GET_BD_LENGTH( bd ); + } + /* + * Unmask RXF (Full frame received) event + */ + mcdma_glue_irq_enable(sc->rxDmaChan); + + rtems_bsdnet_event_receive (INTERRUPT_EVENT | FATAL_INT_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, &events); + if (events & FATAL_INT_EVENT) { + /* + * fatal interrupt ocurred, so reinit fec and restart mcdma tasks + */ + mcf548x_fec_restart(sc); + rxBdIndex = 0; + } + } +} + + +/* + * Function: mcf548x_fec_initialize_hardware + * + * Description: Configure the MCF548X FEC registers and enable the + * SmartComm tasks. This function "turns on" the driver. + * + * Returns: void + * + * Notes: + * + */ +static void mcf548x_fec_initialize_hardware(struct mcf548x_enet_struct *sc) + { + int chan = sc->chan; + + /* + * Reset mcf548x FEC + */ + mcf548x_fec_reset(sc); + + /* + * Clear FEC-Lite interrupt event register (IEVENT) + */ + MCF548X_FEC_EIR(chan) = MCF548X_FEC_EIR_CLEAR_ALL; + + /* + * Set interrupt mask register + */ + MCF548X_FEC_EIMR(chan) = FEC_INTR_MASK_USED; + /* + * Set FEC-Lite receive control register (R_CNTRL) + * frame length=1518, MII mode for 18-wire-transceiver + */ + MCF548X_FEC_RCR(chan) = (MCF548X_FEC_RCR_MAX_FL(ETHER_MAX_LEN) + | MCF548X_FEC_RCR_FCE + | MCF548X_FEC_RCR_MII_MODE); + + /* + * Set FEC-Lite transmit control register (X_CNTRL) + * full-duplex, heartbeat disabled + */ + MCF548X_FEC_TCR(chan) = MCF548X_FEC_TCR_FDEN; + + + + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock(33Mhz) + * and do not drop the Preamble. + */ + MCF548X_FEC_MSCR(chan) = MCF548X_FEC_MSCR_MII_SPEED(7); /* ipb_clk = 33 MHz */ + + /* + * Set Opcode/Pause Duration Register + */ + MCF548X_FEC_PAUR(chan) = 0x00010020; + + /* + * Set Rx FIFO alarm and granularity value + */ + MCF548X_FEC_FECRFCR(chan) = (MCF548X_FEC_FECRFCR_FRM + | MCF548X_FEC_FECRFCR_GR(0x7)); + MCF548X_FEC_FECRFAR(chan) = MCF548X_FEC_FECRFAR_ALARM(256); + + /* + * Set Tx FIFO granularity value + */ + MCF548X_FEC_FECTFCR(chan) = (MCF548X_FEC_FECTFCR_FRM + | MCF548X_FEC_FECTFCR_GR(7)); + + /* + * Set transmit fifo watermark register (X_WMRK), default = 64 + */ + MCF548X_FEC_FECTFAR(chan) = MCF548X_FEC_FECTFAR_ALARM(256); /* 256 bytes */ + MCF548X_FEC_FECTFWR(chan) = MCF548X_FEC_FECTFWR_X_WMRK_64; /* 64 bytes */ + + /* + * Set individual address filter for unicast address + * and set physical address registers. + */ + mcf548x_eth_addr_filter_set(sc); + + /* + * Set multicast address filter + */ + MCF548X_FEC_GAUR(chan) = 0x00000000; + MCF548X_FEC_GALR(chan) = 0x00000000; + + /* + * enable CRC in finite state machine register + */ + MCF548X_FEC_CTCWR(chan) = MCF548X_FEC_CTCWR_TFCW | MCF548X_FEC_CTCWR_CRC; + } + + /* + * Initialize PHY(LXT971A): + * + * Generally, on power up, the LXT971A reads its configuration + * pins to check for forced operation, If not cofigured for + * forced operation, it uses auto-negotiation/parallel detection + * to automatically determine line operating conditions. + * If the PHY device on the other side of the link supports + * auto-negotiation, the LXT971A auto-negotiates with it + * using Fast Link Pulse(FLP) Bursts. If the PHY partner does not + * support auto-negotiation, the LXT971A automatically detects + * the presence of either link pulses(10Mbps PHY) or Idle + * symbols(100Mbps) and sets its operating conditions accordingly. + * + * When auto-negotiation is controlled by software, the following + * steps are recommended. + * + * Note: + * The physical address is dependent on hardware configuration. + * + * Returns: void + * + * Notes: + * + */ +static void mcf548x_fec_initialize_phy(struct mcf548x_enet_struct *sc) + { + int timeout; + unsigned short phyAddr = 0; + int chan = sc->chan; + + /* + * Reset PHY, then delay 300ns + */ + mcf548x_eth_mii_write(sc, phyAddr, 0x0, 0x8000); + + rtems_task_wake_after(2); + + /* MII100 */ + + /* + * Set the auto-negotiation advertisement register bits + */ + mcf548x_eth_mii_write(sc, phyAddr, 0x4, 0x01e1); + + /* + * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation + */ + mcf548x_eth_mii_write(sc, phyAddr, 0x0, 0x1200); + + /* + * Wait for AN completion + */ + timeout = 0x100; +#if 0 + do + { + + rtems_task_wake_after(2); + + if((timeout--) == 0) + { + +#if defined(ETH_DEBUG) + printf ("MCF548XFEC PHY auto neg failed." "\r\n"); +#endif + + } + + if(mcf548x_eth_mii_read(sc, phyAddr, 0x1, &phyStatus) != true) + { + +#if defined(ETH_DEBUG) + printf ("MCF548XFEC PHY auto neg failed: 0x%04x." "\r\n", phyStatus); +#endif + + return; + + } + + } while((phyStatus & 0x0020) != 0x0020); + +#endif +#if ETH_PROMISCOUS_MODE + MCF548X_FEC_RCR(chan) |= MCF548X_FEC_RCR_PROM; /* set to promiscous mode */ +#endif + +#if ETH_LOOP_MODE + MCF548X_FEC_RCR(chan) |= MCF548X_FEC_RCR_LOOP; /* set to loopback mode */ +#endif + +#if defined(ETH_DEBUG) + int i; + unsigned short phyStatus; + /* + * Print PHY registers after initialization. + */ + for(i = 0; i < 9; i++) + { + + mcf548x_eth_mii_read(sc, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x" "\r\n", i, phyStatus); + + } + + for(i = 16; i < 21; i++) + { + + mcf548x_eth_mii_read(sc, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x" "\r\n", i, phyStatus); + + } +#endif /* ETH_DEBUG */ + + } + + +/* + * Send packet (caller provides header). + */ +static void mcf548x_fec_tx_start(struct ifnet *ifp) + { + + struct mcf548x_enet_struct *sc = ifp->if_softc; + + ifp->if_flags |= IFF_OACTIVE; + + rtems_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT); + + } + + +/* + * start the DMA channel + */ +static void mcf548x_fec_startDMA(struct mcf548x_enet_struct *sc) +{ + int chan = sc->chan; + int mcdma_rc; + /* + * Enable the SmartDMA receive task. + */ + mcdma_rc = MCD_startDma + (sc->rxDmaChan, /* the channel on which to run the DMA */ + (void *)sc->rxBd, /* the address to move data from, or buffer-descriptor addr */ + 0, /* the amount to increment the source address per transfer */ + (void *)&MCF548X_FEC_FECRFDR(chan), /* the address to move data to */ + 0, /* the amount to increment the destination address per transfer */ +#if 0 + 4, /* the number of bytes to transfer independent of the transfer size */ +#else + ETHER_MAX_LEN, /* the number of bytes to transfer independent of the transfer size */ +#endif + 0, /* the number bytes in of each data movement (1, 2, or 4) */ + MCF548X_FEC_RX_INITIATOR(chan), /* what device initiates the DMA */ + 2, /* priority of the DMA */ + 0 /* flags describing the DMA */ + | MCD_FECRX_DMA + | MCD_INTERRUPT + | MCD_TT_FLAGS_CW + | MCD_TT_FLAGS_RL + | MCD_TT_FLAGS_SP + , + 0 /* a description of byte swapping, bit swapping, and CRC actions */ + | MCD_NO_CSUM + | MCD_NO_BYTE_SWAP + ); + if (mcdma_rc != MCD_OK) { + rtems_panic("FEC: cannot start rx DMA"); + } + mcdma_rc = MCD_startDma + (sc->txDmaChan, /* the channel on which to run the DMA */ + (void *)sc->txBd, /* the address to move data from, or buffer-descriptor addr */ + 0, /* the amount to increment the source address per transfer */ + (void *)&MCF548X_FEC_FECTFDR(chan), /* the address to move data to */ + 0, /* the amount to increment the destination address per transfer */ +#if 0 + 4, /* the number of bytes to transfer independent of the transfer size */ +#else + ETHER_MAX_LEN, /* the number of bytes to transfer independent of the transfer size */ +#endif + 0, /* the number bytes in of each data movement (1, 2, or 4) */ + MCF548X_FEC_TX_INITIATOR(chan), /* what device initiates the DMA */ + 1, /* priority of the DMA */ + 0 /* flags describing the DMA */ + | MCD_FECTX_DMA + | MCD_INTERRUPT + | MCD_TT_FLAGS_CW + | MCD_TT_FLAGS_RL + | MCD_TT_FLAGS_SP + , + 0 /* a description of byte swapping, bit swapping, and CRC actions */ + | MCD_NO_CSUM + | MCD_NO_BYTE_SWAP + ); + if (mcdma_rc != MCD_OK) { + rtems_panic("FEC: cannot start tx DMA"); + } +} +/* + * Initialize and start the device + */ +static void mcf548x_fec_init(void *arg) +{ + struct mcf548x_enet_struct *sc = (struct mcf548x_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + int chan = sc->chan; + rtems_isr_entry old_handler; + char *txTaskName = "FTx0"; + char *rxTaskName = "FRx0"; + if(sc->txDaemonTid == 0) + { + /* + * Allocate a set of BDs + */ + sc->rxBd = SRAM_RXBD_BASE(_SysSramBase,chan); + sc->txBd = SRAM_TXBD_BASE(_SysSramBase,chan); + + if(!sc->rxBd || !sc->txBd) + rtems_panic ("No memory for BDs"); + /* + * clear the BDs + */ + memset((void *)sc->rxBd,0,sc->rxBdCount * sizeof *(sc->rxBd)); + memset((void *)sc->txBd,0,sc->txBdCount * sizeof *(sc->txBd)); + /* + * Allocate a set of mbuf pointers + */ + sc->rxMbuf = + malloc(sc->rxBdCount * sizeof *sc->rxMbuf, M_MBUF, M_NOWAIT); + sc->txMbuf = + malloc(sc->txBdCount * sizeof *sc->txMbuf, M_MBUF, M_NOWAIT); + + if(!sc->rxMbuf || !sc->txMbuf) + rtems_panic ("No memory for mbuf pointers"); + + sc->txDmaChan = MCDMA_FEC_TX_CHAN(chan); + sc->rxDmaChan = MCDMA_FEC_RX_CHAN(chan); + + mcdma_glue_init(SRAM_DMA_BASE(_SysSramBase)); + + /* + * Set up interrupts + */ + mcdma_glue_irq_install(sc->rxDmaChan, + mcf548x_mcdma_rx_irq_handler, + sc); + mcdma_glue_irq_install(sc->txDmaChan, + mcf548x_mcdma_tx_irq_handler, + sc); + if(rtems_interrupt_catch(mcf548x_fec_irq_handler, + MCF548X_FEC_IRQ_VECTOR(chan), + &old_handler)) { + rtems_panic ("Can't attach MFC54xx FEX interrupt handler\n"); + } + + MCF548X_INTC_ICRn(MCF548X_FEC_IRQ_VECTOR(chan) % 64) = + MCF548X_INTC_ICRn_IL(FEC_IRQ_LEVEL) | + MCF548X_INTC_ICRn_IP(FEC_IRQ_PRIORITY); + + MCF548X_INTC_IMRH &= ~(1 << (MCF548X_FEC_IRQ_VECTOR(chan) % 32)); + + MCF548X_FEC_EIMR(chan) = FEC_INTR_MASK_USED; + mcf548x_fec_rx_bd_init(sc); + + /* + * reset and Set up mcf548x FEC hardware + */ + mcf548x_fec_initialize_hardware(sc); + /* + * Set up the phy + */ + mcf548x_fec_initialize_phy(sc); + + /* + * Start driver tasks + */ + txTaskName[3] = '0'+chan; + rxTaskName[3] = '0'+chan; + sc->txDaemonTid = rtems_bsdnet_newproc(txTaskName, 4096, + mcf548x_fec_txDaemon, sc); + sc->rxDaemonTid = rtems_bsdnet_newproc(rxTaskName, 4096, + mcf548x_fec_rxDaemon, sc); + /* + * Clear SmartDMA task interrupt pending bits. + */ + MCDMA_CLR_PENDING(sc->rxDmaChan ); + MCDMA_CLR_PENDING(sc->txDmaChan ); + + /* + * start the DMA channels + */ + mcf548x_fec_startDMA(sc); + /* + * Enable FEC-Lite controller + */ + MCF548X_FEC_ECR(chan) |= MCF548X_FEC_ECR_ETHER_EN; + + + } + + /* + * Set flags appropriately + */ + if(ifp->if_flags & IFF_PROMISC) + MCF548X_FEC_RCR(chan) |= MCF548X_FEC_RCR_PROM; + else + MCF548X_FEC_RCR(chan) &= ~MCF548X_FEC_RCR_PROM; + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; +} + + +static void enet_stats (struct mcf548x_enet_struct *sc) +{ + printf (" Rx Interrupts:%-8lu", sc->rxInterrupts); + printf (" Not First:%-8lu", sc->rxNotFirst); + printf (" Not Last:%-8lu\n", sc->rxNotLast); + printf (" Giant:%-8lu", sc->rxGiant); + printf (" Non-octet:%-8lu\n", sc->rxNonOctet); + printf (" Bad CRC:%-8lu", sc->rxBadCRC); + printf (" Overrun:%-8lu", sc->rxOverrun); + printf (" Collision:%-8lu\n", sc->rxCollision); + + printf (" Tx Interrupts:%-8lu", sc->txInterrupts); + printf (" Deferred:%-8lu", sc->txDeferred); + printf (" Late Collision:%-8lu\n", sc->txLateCollision); + printf (" Retransmit Limit:%-8lu", sc->txRetryLimit); + printf (" Underrun:%-8lu", sc->txUnderrun); + printf (" Misaligned:%-8lu\n", sc->txMisaligned); + +} + +/* + * restart the driver, reinit the fec + * this function is responsible to reinitialize the FEC in case a fatal + * error has ocurred. This is needed, wen a RxFIFO Overrun or a TxFIFO underrun + * has ocurred. In these cases, the FEC is automatically disabled, and + * both FIFOs must be reset and the BestComm tasks must be restarted + * + * Note: the daemon tasks will continue to run + * (in fact this function will be called in the context of the rx daemon task) + */ +#define NEW_DMA_SETUP + +static void mcf548x_fec_restart(struct mcf548x_enet_struct *sc) +{ + int chan = sc->chan; + /* + * FIXME: bring Tx Daemon into idle state + */ +#ifdef NEW_DMA_SETUP + /* + * cleanup remaining receive mbufs + */ + mcf548x_fec_rx_bd_cleanup(sc); +#endif + /* + * Stop DMA tasks + */ + MCD_killDma (sc->rxDmaChan); + MCD_killDma (sc->txDmaChan); + /* + * FIXME: wait, until Tx Daemon is in idle state + */ + + /* + * Disable transmit / receive interrupts + */ + mcdma_glue_irq_disable(sc->txDmaChan); + mcdma_glue_irq_disable(sc->rxDmaChan); +#ifdef NEW_DMA_SETUP + /* + * recycle pending tx buffers + * FIXME: try to extract pending Tx buffers + */ +#if 0 + mcf548x_fec_tx_bd_requeue(sc); +#else + mcf548x_fec_retire_tbd(sc,true); +#endif +#endif + /* + * re-initialize the FEC hardware + */ + mcf548x_fec_initialize_hardware(sc); + +#ifdef NEW_DMA_SETUP + + /* + * reinit receive mbufs + */ + mcf548x_fec_rx_bd_init(sc); +#endif + /* + * Clear SmartDMA task interrupt pending bits. + */ + MCDMA_CLR_PENDING( sc->rxDmaChan ); + + /* + * start the DMA channels again + */ + mcf548x_fec_startDMA(sc); + /* + * reenable rx/tx interrupts + */ + mcdma_glue_irq_enable(sc->rxDmaChan); + mcdma_glue_irq_enable(sc->txDmaChan); + /* + * (re-)init fec hardware + */ + mcf548x_fec_initialize_hardware(sc); + /* + * reenable fec FIFO error interrupts + */ + MCF548X_FEC_EIMR(chan) = FEC_INTR_MASK_USED; + /* + * Enable FEC-Lite controller + */ + MCF548X_FEC_ECR(chan) |= MCF548X_FEC_ECR_ETHER_EN; +} + +int32_t mcf548x_fec_setMultiFilter(struct ifnet *ifp) +{ + /*struct mcf548x_enet_struct *sc = ifp->if_softc; */ + /* XXX anything to do? */ + return 0; +} + + +/* + * Driver ioctl handler + */ +static int mcf548x_fec_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data) + { + struct mcf548x_enet_struct *sc = ifp->if_softc; + int error = 0; + + switch(command) + { + + case SIOCGIFADDR: + case SIOCSIFADDR: + + ether_ioctl(ifp, command, data); + + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: { + struct ifreq* ifr = (struct ifreq*) data; + error = (command == SIOCADDMULTI) + ? ether_addmulti(ifr, &sc->arpcom) + : ether_delmulti(ifr, &sc->arpcom); + + if (error == ENETRESET) { + if (ifp->if_flags & IFF_RUNNING) + error = mcf548x_fec_setMultiFilter(ifp); + else + error = 0; + } + break; + } + + case SIOCSIFFLAGS: + + switch(ifp->if_flags & (IFF_UP | IFF_RUNNING)) + { + + case IFF_RUNNING: + + mcf548x_fec_off(sc); + + break; + + case IFF_UP: + + mcf548x_fec_init(sc); + + break; + + case IFF_UP | IFF_RUNNING: + + mcf548x_fec_off(sc); + mcf548x_fec_init(sc); + + break; + + default: + break; + + } + + break; + + case SIO_RTEMS_SHOW_STATS: + + enet_stats(sc); + + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + + error = EINVAL; + + break; + + } + + return error; + + } + + +/* + * Attach the MCF548X fec driver to the system + */ +int rtems_mcf548x_fec_driver_attach(struct rtems_bsdnet_ifconfig *config) + { + struct mcf548x_enet_struct *sc; + struct ifnet *ifp; + int mtu; + int unitNumber; + char *unitName; + + /* + * Parse driver name + */ + if((unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) < 0) + return 0; + + /* + * Is driver free? + */ + if ((unitNumber <= 0) || (unitNumber > NIFACES)) + { + + printf ("Bad FEC unit number.\n"); + return 0; + + } + + sc = &enet_driver[unitNumber - 1]; + sc->chan = unitNumber-1; + ifp = &sc->arpcom.ac_if; + + if(ifp->if_softc != NULL) + { + + printf ("Driver already in use.\n"); + return 0; + + } + + /* + * Process options + */ +#if NVRAM_CONFIGURE == 1 + + /* Configure from NVRAM */ + if(addr = nvram->ipaddr) + { + + /* We have a non-zero entry, copy the value */ + if(pAddr = malloc(INET_ADDR_MAX_BUF_SIZE, 0, M_NOWAIT)) + config->ip_address = (char *)inet_ntop(AF_INET, &addr, pAddr, INET_ADDR_MAX_BUF_SIZE -1); + else + rtems_panic("Can't allocate ip_address buffer!\n"); + + } + + if(addr = nvram->netmask) + { + + /* We have a non-zero entry, copy the value */ + if (pAddr = malloc (INET_ADDR_MAX_BUF_SIZE, 0, M_NOWAIT)) + config->ip_netmask = (char *)inet_ntop(AF_INET, &addr, pAddr, INET_ADDR_MAX_BUF_SIZE -1); + else + rtems_panic("Can't allocate ip_netmask buffer!\n"); + + } + + /* Ethernet address requires special handling -- it must be copied into + * the arpcom struct. The following if construct serves only to give the + * User Area NVRAM parameter the highest priority. + * + * If the ethernet address is specified in NVRAM, go ahead and copy it. + * (ETHER_ADDR_LEN = 6 bytes). + */ + if(nvram->enaddr[0] || nvram->enaddr[1] || nvram->enaddr[2]) + { + + /* Anything in the first three bytes indicates a non-zero entry, copy value */ + memcpy((void *)sc->arpcom.ac_enaddr, &nvram->enaddr, ETHER_ADDR_LEN); + + } + else + if(config->hardware_address) + { + + /* There is no entry in NVRAM, but there is in the ifconfig struct, so use it. */ + memcpy((void *)sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + } + else + { + /* There is no ethernet address provided, so it could be read + * from the Ethernet protocol block of SCC1 in DPRAM. + */ + rtems_panic("No Ethernet address specified!\n"); + } + +#else /* NVRAM_CONFIGURE != 1 */ + + if(config->hardware_address) + { + + memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + + } + else + { + + /* There is no ethernet address provided, so it could be read + * from the Ethernet protocol block of SCC1 in DPRAM. + */ + rtems_panic("No Ethernet address specified!\n"); + + } + +#endif /* NVRAM_CONFIGURE != 1 */ +#ifdef HAS_UBOOT + if ((sc->arpcom.ac_enaddr[0] == 0) && + (sc->arpcom.ac_enaddr[1] == 0) && + (sc->arpcom.ac_enaddr[2] == 0)) { + memcpy( + (void *)sc->arpcom.ac_enaddr, + bsp_uboot_board_info.bi_enetaddr, + ETHER_ADDR_LEN + ); + } +#endif + if(config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + + if(config->rbuf_count) + sc->rxBdCount = config->rbuf_count; + else + sc->rxBdCount = RX_BUF_COUNT; + + if(config->xbuf_count) + sc->txBdCount = config->xbuf_count; + else + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + + sc->acceptBroadcast = !config->ignore_broadcast; + + /* + * Set up network interface values + */ + ifp->if_softc = sc; + ifp->if_unit = unitNumber; + ifp->if_name = unitName; + ifp->if_mtu = mtu; + ifp->if_init = mcf548x_fec_init; + ifp->if_ioctl = mcf548x_fec_ioctl; + ifp->if_start = mcf548x_fec_tx_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST; + /*ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;*/ + + if(ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + /* + * Attach the interface + */ + if_attach(ifp); + + ether_ifattach(ifp); + + return 1; + } + + +int rtems_mcf548x_fec_driver_attach_detach(struct rtems_bsdnet_ifconfig *config, int attaching) +{ + if (attaching) { + return rtems_mcf548x_fec_driver_attach(config); + } + else { + return 0; + } +} + + diff --git a/c/src/lib/libbsp/m68k/genmcf548x/startup/init548x.c b/c/src/lib/libbsp/m68k/genmcf548x/startup/init548x.c index faaec01c66..342cd3e9f8 100644 --- a/c/src/lib/libbsp/m68k/genmcf548x/startup/init548x.c +++ b/c/src/lib/libbsp/m68k/genmcf548x/startup/init548x.c @@ -301,12 +301,12 @@ gpio_init(void) * erroneous transmissions */ MCF548X_GPIO_PAR_FECI2CIRQ = (0 + | MCF548X_GPIO_PAR_FECI2CIRQ_PAR_E1MDC_EMDC | MCF548X_GPIO_PAR_FECI2CIRQ_PAR_E1MDIO_EMDIO | MCF548X_GPIO_PAR_FECI2CIRQ_PAR_E1MII | MCF548X_GPIO_PAR_FECI2CIRQ_PAR_E17 - ); - MCF548X_GPIO_PAR_FECI2CIRQ = (0 + | MCF548X_GPIO_PAR_FECI2CIRQ_PAR_E0MDC | MCF548X_GPIO_PAR_FECI2CIRQ_PAR_E0MDIO | MCF548X_GPIO_PAR_FECI2CIRQ_PAR_E0MII |