From ca680bc5890abe0d6bfe7eb4a40a0229f1b6bd36 Mon Sep 17 00:00:00 2001 From: Ralf Corsepius Date: Sat, 31 Dec 2005 05:09:26 +0000 Subject: New (CVS import Thomas Doerfler 's submission). --- .../libbsp/powerpc/gen5200/network_5200/network.c | 1596 ++++++++++++++++++++ 1 file changed, 1596 insertions(+) create mode 100644 c/src/lib/libbsp/powerpc/gen5200/network_5200/network.c (limited to 'c/src/lib/libbsp/powerpc/gen5200/network_5200') diff --git a/c/src/lib/libbsp/powerpc/gen5200/network_5200/network.c b/c/src/lib/libbsp/powerpc/gen5200/network_5200/network.c new file mode 100644 index 0000000000..3fa52f7b72 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/gen5200/network_5200/network.c @@ -0,0 +1,1596 @@ +/*===============================================================*\ +| Project: RTEMS generic MPC5200 BSP | ++-----------------------------------------------------------------+ +| File: $File$ ++-----------------------------------------------------------------+ +| Partially based on the code references which are named below. | +| Adaptions, modifications, enhancements and any recent parts of | +| the code are: | +| Copyright (c) 2005 | +| 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 | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 01.12.05 creation doe | +|*****************************************************************| +|*CVS information: | +|*(the following information is created automatically, | +|*do not edit here) | +|*****************************************************************| +|* $Log$ +|* Revision 1.10 2005/12/06 14:30:42 thomas +|* updated name for peripheral register block +|* +|* Revision 1.9 2005/12/06 14:11:12 thomas +|* added EB file headers +|* + * +|*****************************************************************| +\*===============================================================*/ +/* + * RTEMS/TCPIP driver for MPC5200 FEC Ethernet + * + * Modified for Motorola MPC5200 by Thomas Doerfler, + * COPYRIGHT (c) 2003, IMD + * + * Modified for Motorola IceCube (mgt5100) by Peter Rasmussen + * 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 MPC860 by Jay Monkman (jmonkman@frasca.com) + * + * This supports Ethernet on either SCC1 or the FEC of the MPC860T. + * Right now, we only do 10 Mbps, even with the FEC. The function + * rtems_enet_driver_attach determines which one to use. Currently, + * only one may be used at a time. + * + * Based on the MC68360 network driver by + * W. Eric Norum + * Saskatchewan Accelerator Laboratory + * University of Saskatchewan + * Saskatoon, Saskatchewan, CANADA + * eric@skatter.usask.ca + * + * This supports ethernet on SCC1. Right now, we only do 10 Mbps. + * + * Modifications by Darlene Stewart + * and Charles-Antoine Gauthier + * Copyright (c) 1999, National Research Council of Canada + * + * network.c,v 1.7 2001/08/31 14:57:48 joel Exp + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/bsp.h" +#include "../irq/irq.h" +#include "../include/mpc5200.h" +#include +#include + +/* motorola-capi-specifics... */ +#include "../bestcomm/include/ppctypes.h" /* uint32, et. al. */ +#include "../bestcomm/dma_image.h" +#include "../bestcomm/bestcomm_glue.h" + + +#define SDMA_BD_TFD 0x08000000 /*< Transmit Frame Done */ +#define SDMA_BD_INT 0x04000000 /*< Interrupt on frame done */ +#define SDMA_BD_RX_NUM 32 /* Number of receive buffer descriptors */ +#define SDMA_BD_TX_NUM 48 /* Number of transmit buffer descriptors */ + +#define SET_BD_STATUS(bd, stat) { \ + (bd)->Status &= 0x0000ffff; \ + (bd)->Status |= 0xffff0000 & stat; \ +} +#define SET_BD_LENGTH(bd, len) { \ + (bd)->Status &= 0xffff0000; \ + (bd)->Status |= 0x0000ffff & len; \ +} +#define GET_BD_STATUS(bd) ((bd)->Status & 0xffff0000) +#define GET_BD_LENGTH(bd) ((bd)->Status & 0x0000ffff) +#define GET_SDMA_PENDINGBIT(Bit) \ + (mpc5200.IntPend & (uint32)(1<arpcom.ac_if; + BDIdx bdi; + + /* + * 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; + bdi = TaskBDAssign( rxTaskId, + mtod(m, void *), + NULL, + ETHER_MAX_LEN, + 0 ); + if (bdi != rxBdIndex) { + rtems_panic("network rx buffer indices out of sync"); + } + } +} + +/* + * Function: MPC5200_eth_addr_filter_set + * + * Description: Set individual address filter for unicast address and + * set physical address registers. + * + * Returns: void + * + * Notes: + * + */ +static void mpc5200_eth_addr_filter_set(struct mpc5200_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 */ + + /* + * 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) + { + + mpc5200.iaddr1 = (1 << (crc - 32)); + mpc5200.iaddr2 = 0; + + } + else + { + + mpc5200.iaddr1 = 0; + mpc5200.iaddr2 = (1 << crc); + + } + + /* + * Set physical address + */ + mpc5200.paddr1 = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; + mpc5200.paddr2 = (mac[4] << 24) + (mac[5] << 16) + 0x8808; + + } + + +/* + * Function: mpc5200_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 mpc5200_eth_mii_read(struct mpc5200_enet_struct *sc, unsigned char phyAddr, unsigned char regAddr, unsigned short * retVal) + { + unsigned long reg; /* convenient holder for the PHY register */ + unsigned long phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + reg = regAddr << MPC5200_FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << MPC5200_FEC_MII_DATA_PA_SHIFT; + + mpc5200.mii_data = (MPC5200_FEC_MII_DATA_ST | MPC5200_FEC_MII_DATA_OP_RD | MPC5200_FEC_MII_DATA_TA | phy | reg); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(mpc5200.ievent & 0x00800000))); + + if(timeout == 0) + { + +#ifdef ETH_DEBUG + printf ("Read MDIO failed..." "\r\n"); +#endif + + return FALSE; + + } + + /* + * clear mii interrupt bit + */ + mpc5200.ievent = 0x00800000; + + /* + * it's now safe to read the PHY's register + */ + *retVal = (unsigned short)mpc5200.mii_data; + + return TRUE; + + } + +/* + * Function: mpc5200_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 mpc5200_eth_mii_write(struct mpc5200_enet_struct *sc, unsigned char phyAddr, unsigned char regAddr, unsigned short data) + { + unsigned long reg; /* convenient holder for the PHY register */ + unsigned long phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + reg = regAddr << MPC5200_FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << MPC5200_FEC_MII_DATA_PA_SHIFT; + + mpc5200.mii_data = (MPC5200_FEC_MII_DATA_ST | MPC5200_FEC_MII_DATA_OP_WR | MPC5200_FEC_MII_DATA_TA | phy | reg | data); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(mpc5200.ievent & 0x00800000))); + + if(timeout == 0) + { + +#ifdef ETH_DEBUG + printf ("Write MDIO failed..." "\r\n"); +#endif + + return FALSE; + + } + + /* + * clear MII interrupt bit + */ + mpc5200.ievent = 0x00800000; + + return TRUE; + + } + + +/* + * Function: mpc5200_fec_reset + * + * Description: Reset a running ethernet driver including the hardware + * FIFOs and the FEC. + * + * Returns: Success (boolean) + * + * Notes: + * + */ +static int mpc5200_fec_reset(struct mpc5200_enet_struct *sc) { + /* + * Clear FIFO status registers + */ + mpc5200.rfifo_status &= FEC_FIFO_STAT_ERROR; + mpc5200.tfifo_status &= FEC_FIFO_STAT_ERROR; + + /* + * + */ + mpc5200.reset_cntrl = 0x01000000; + + /* + * Issue a reset command to the FEC chip + */ + mpc5200.ecntrl |= FEC_ECNTRL_RESET; + + /* + * wait at least 16 clock cycles + */ + rtems_task_wake_after(2); + + return TRUE; +} + + +/* + * Function: mpc5200_fec_off + * + * Description: Stop the FEC and disable the ethernet SmartComm tasks. + * This function "turns off" the driver. + * + * Returns: void + * + * Notes: + * + */ +void mpc5200_fec_off(struct mpc5200_enet_struct *sc) + { + int counter = 0xffff; + +#if defined(ETH_DEBUG) + unsigned short phyStatus, i; + unsigned char phyAddr = 0; + + for(i = 0; i < 9; i++) + { + + mpc5200_eth_mii_read(sc, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x" "\r\n", i, phyStatus); + + } + + for(i = 16; i < 21; i++) + { + + mpc5200_eth_mii_read(sc, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x" "\r\n", i, phyStatus); + + } +#endif /* ETH_DEBUG */ + + /* + * mask FEC chip interrupts + */ + mpc5200.imask = FEC_INTR_MASK_ALL; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + mpc5200.x_cntrl |= FEC_XCNTRL_GTS; + + /* + * wait for graceful stop to register + */ + while((counter--) && (!(mpc5200.ievent & FEC_INTR_GRA))); + + /* + * Disable the SmartDMA transmit and receive tasks. + */ + TaskStop( rxTaskId ); + TaskStop( txTaskId ); + /* + * Disable transmit / receive interrupts + */ + bestcomm_glue_irq_disable(FEC_XMIT_TASK_NO); + bestcomm_glue_irq_disable(FEC_RECV_TASK_NO); + + /* + * Disable the Ethernet Controller + */ + mpc5200.ecntrl &= ~(FEC_ECNTRL_OE | FEC_ECNTRL_EN); + + } + + +/* + * MPC5200 SmartComm ethernet interrupt handler + */ +void mpc5200_smartcomm_rx_irq_handler(rtems_irq_hdl_param unused) + { + volatile uint32_t ievent; + + ievent = mpc5200.ievent; + + + /* Frame received? */ + if(GET_SDMA_PENDINGBIT(FEC_RECV_TASK_NO)) + { + + SDMA_CLEAR_IEVENT(&mpc5200.IntPend,FEC_RECV_TASK_NO); + + bestcomm_glue_irq_disable(FEC_RECV_TASK_NO);/*Disable receive ints*/ + + enet_driver[0].rxInterrupts++; /* Rx int has occurred */ + + rtems_event_send(enet_driver[0].rxDaemonTid, INTERRUPT_EVENT); + + } + } + +/* + * MPC5200 SmartComm ethernet interrupt handler + */ +void mpc5200_smartcomm_tx_irq_handler(rtems_irq_hdl_param unused) + { + volatile uint32_t ievent; + + ievent = mpc5200.ievent; + + + /* Buffer transmitted or transmitter error? */ + if(GET_SDMA_PENDINGBIT(FEC_XMIT_TASK_NO)) + { + + SDMA_CLEAR_IEVENT(&mpc5200.IntPend,FEC_XMIT_TASK_NO); + + bestcomm_glue_irq_disable(FEC_XMIT_TASK_NO);/*Disable tx ints*/ + + enet_driver[0].txInterrupts++; /* Tx int has occurred */ + + rtems_event_send(enet_driver[0].txDaemonTid, INTERRUPT_EVENT); + + } + + } + + + + + + /* + * Function: mpc5200_fec_retire_tbd + * + * Description: Soak up buffer descriptors that have been sent. + * + * Returns: void + * + * Notes: + * + */ +static void mpc5200_fec_retire_tbd(struct mpc5200_enet_struct *sc) +{ + struct mbuf *n; + TaskBD1_t *bdRing = (TaskBD1_t *)TaskGetBDRing( txTaskId );; + /* + * Clear already transmitted BDs first. Will not work calling same + * from fecExceptionHandler(TFINT). + */ + + while ((sc->txBdActiveCount > 0) && + (bdRing[sc->txBdTail].Status == 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; + } + } +} + + +static void mpc5200_fec_sendpacket(struct ifnet *ifp,struct mbuf *m) { + struct mpc5200_enet_struct *sc = ifp->if_softc; + struct mbuf *l = NULL; + int nAdded; + uint32_t status; + rtems_event_set events; + TaskBD1_t *bdRing = (TaskBD1_t *)TaskGetBDRing( txTaskId ); + TaskBD1_t *thisBd; + TaskBD1_t *firstBd = NULL; + void *data_ptr; + size_t data_len; + + /* + * Free up buffer descriptors + */ + mpc5200_fec_retire_tbd(sc); + + /* + * 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 + */ + SDMA_CLEAR_IEVENT(&mpc5200.IntPend,FEC_XMIT_TASK_NO); + + /* + * 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. + */ + mpc5200_fec_retire_tbd(sc); + + while((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + bestcomm_glue_irq_enable(FEC_XMIT_TASK_NO); + rtems_bsdnet_event_receive(INTERRUPT_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, &events); + mpc5200_fec_retire_tbd(sc); + } + } + + 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 = bdRing + 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) + ? TASK_BD_TFD | TASK_BD_INT + : 0); + /* + * Don't set the READY flag till the + * whole packet has been readied. + */ + if (firstBd != NULL) { + status |= (uint32)SDMA_BD_MASK_READY; + } + else { + firstBd = thisBd; + } + + data_ptr = mtod(m, void *); + data_len = (uint32)m->m_len; + sc->txMbuf[sc->txBdHead] = m; + /* go to next part in chain */ + l = m; + m = m->m_next; + + thisBd->DataPtr[0] = (uint32)data_ptr; + thisBd->Status = (status + |((uint32)SDMA_DRD_MASK_LENGTH & data_len)); + + 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) { + firstBd->Status |= SDMA_BD_MASK_READY; + SDMA_TASK_ENABLE(SDMA_TCR, txTaskId); + sc->txBdActiveCount += nAdded; + } + break; + } + } /* end of for(;;) */ +} + + +/* + * Driver transmit daemon + */ +void mpc5200_fec_txDaemon(void *arg) + { + struct mpc5200_enet_struct *sc = (struct mpc5200_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for(;;) + { + /* + * Wait for packet + */ + 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; + + mpc5200_fec_sendpacket(ifp, m); + + } + + ifp->if_flags &= ~IFF_OACTIVE; + + } + + } + + +/* + * reader task + */ +static void mpc5200_fec_rxDaemon(void *arg){ + struct mpc5200_enet_struct *sc = (struct mpc5200_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; + uint16 len = 1; + TaskBD1_t *bd; + void *dptr; + TaskBD1_t *bdRing = (TaskBD1_t *)TaskGetBDRing( rxTaskId );; + + /* + * Input packet handling loop + */ + rxBdIndex = 0; + + for (;;) { + /* + * Clear old events + */ + SDMA_CLEAR_IEVENT(&mpc5200.IntPend,FEC_RECV_TASK_NO); + /* + * Get the first BD pointer and its length. + */ + bd = bdRing + rxBdIndex; + status = bd->Status; + len = (uint16)GET_BD_LENGTH( bd ); + + /* + * Loop through BDs until we find an empty one. This indicates that + * the SmartDMA is still using it. + */ + while( !(status & SDMA_BD_MASK_READY) ) { + + /* + * Remember the data pointer from this transfer. + */ + dptr = (void *)bd->DataPtr[0]; + 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; + bd->DataPtr[0] = (uint32)mtod(m, void *); + bd->Status = ( ( (uint32)SDMA_DRD_MASK_LENGTH & (uint32)size) + | ((uint32)SDMA_BD_MASK_READY)); + + /* + * advance to next BD + */ + if (++rxBdIndex >= sc->rxBdCount) { + rxBdIndex = 0; + } + /* + * Get next BD pointer and its length. + */ + bd = bdRing + rxBdIndex; + status = bd->Status; + len = (uint16)GET_BD_LENGTH( bd ); + } + /* + * Unmask RXF (Full frame received) event + */ + bestcomm_glue_irq_enable(FEC_RECV_TASK_NO); + + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, &events); + + } +} + + +/* + * Function: mpc5200_fec_initialize_hardware + * + * Description: Configure the MPC5200 FEC registers and enable the + * SmartComm tasks. This function "turns on" the driver. + * + * Returns: void + * + * Notes: + * + */ +static void mpc5200_fec_initialize_hardware(struct mpc5200_enet_struct *sc) + { + int timeout; + unsigned short phyAddr = 0; + + /* + * Reset mpc5200 FEC + */ + mpc5200_fec_reset(sc); + + /* + * Clear FEC-Lite interrupt event register (IEVENT) + */ + mpc5200.ievent = FEC_INTR_CLEAR_ALL; + + /* + * Set interrupt mask register + */ + mpc5200.imask = (FEC_INTR_HBEEN + | FEC_INTR_BREN + | FEC_INTR_BTEN + | FEC_INTR_GRAEN + | FEC_INTR_LATE_COL + | FEC_INTR_COL_RETRY + | FEC_INTR_XFIFO_UN + | FEC_INTR_XFIFO_ERR + | FEC_INTR_RFIFO_ERR + | FEC_INTR_TFINT + ); + /* + * Set FEC-Lite receive control register (R_CNTRL) + * frame length=1518, MII mode for 18-wire-transceiver + */ + mpc5200.r_cntrl = ((ETHER_MAX_LEN << FEC_RCNTRL_MAX_FL_SHIFT) + | FEC_RCNTRL_FCE + | FEC_RCNTRL_MII_MODE); + + /* + * Set FEC-Lite transmit control register (X_CNTRL) + * full-duplex, heartbeat disabled + */ + mpc5200.x_cntrl = FEC_XCNTRL_FDEN; + + + + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock(33Mhz) + * and do not drop the Preamble. + */ + mpc5200.mii_speed = (7 << 1); /* ipb_clk = 33 MHz */ + + /* + * Set Opcode/Pause Duration Register + */ + mpc5200.op_pause = 0x00010020; + + /* + * Set Rx FIFO alarm and granularity value + */ + mpc5200.rfifo_cntrl = (FEC_FIFO_CNTRL_FRAME + | (0x7 << FEC_FIFO_CNTRL_GR_SHIFT)); + mpc5200.rfifo_alarm = 0x0000030c; + + /* + * Set Tx FIFO granularity value + */ + mpc5200.tfifo_cntrl = (FEC_FIFO_CNTRL_FRAME + | (0x7 << FEC_FIFO_CNTRL_GR_SHIFT)); + + /* + * Set transmit fifo watermark register (X_WMRK), default = 64 + */ + mpc5200.tfifo_alarm = 0x00000100; /* 256 bytes */ + mpc5200.x_wmrk = FEC_XWMRK_256; /* 256 bytes */ + + /* + * Set individual address filter for unicast address + * and set physical address registers. + */ + mpc5200_eth_addr_filter_set(sc); + + /* + * Set multicast address filter + */ + mpc5200.gaddr1 = 0x00000000; + mpc5200.gaddr2 = 0x00000000; + + /* + * enable CRC in finite state machine register + */ + mpc5200.xmit_fsm = FEC_FSM_CRC | FEC_FSM_ENFSM; + + /* + * 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. + * + */ + + /* + * Reset PHY, then delay 300ns + */ + mpc5200_eth_mii_write(sc, phyAddr, 0x0, 0x8000); + + rtems_task_wake_after(2); + + /* MII100 */ + + /* + * Set the auto-negotiation advertisement register bits + */ + mpc5200_eth_mii_write(sc, phyAddr, 0x4, 0x01e1); + + /* + * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation + */ + mpc5200_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 ("MPC5200FEC PHY auto neg failed." "\r\n"); +#endif + + } + + if(mpc5200_eth_mii_read(sc, phyAddr, 0x1, &phyStatus) != TRUE) + { + +#if defined(ETH_DEBUG) + printf ("MPC5200FEC PHY auto neg failed: 0x%04x." "\r\n", phyStatus); +#endif + + return; + + } + + } while((phyStatus & 0x0020) != 0x0020); + +#endif +#if ETH_PROMISCOUS_MODE + mpc5200.r_cntrl |= 0x00000008; // set to promiscous mode +#endif + +#if ETH_LOOP_MODE + mpc5200.r_cntrl |= 0x00000001; // set to loop mode +#endif + +#if defined(ETH_DEBUG) + int i; + /* + * Print PHY registers after initialization. + */ + for(i = 0; i < 9; i++) + { + + mpc5200_eth_mii_read(sc, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x" "\r\n", i, phyStatus); + + } + + for(i = 16; i < 21; i++) + { + + mpc5200_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 mpc5200_fec_tx_start(struct ifnet *ifp) + { + + struct mpc5200_enet_struct *sc = ifp->if_softc; + + ifp->if_flags |= IFF_OACTIVE; + + rtems_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT); + + } + + +/* + * set up sdma tasks for ethernet + */ +static void mpc5200_sdma_task_setup(void) { + TaskSetupParamSet_t rxParam; /* RX task setup parameters */ + TaskSetupParamSet_t txParam; /* TX task setup parameters */ + + /* + * Setup the SDMA RX task. + */ + rxParam.NumBD = SDMA_BD_RX_NUM; + rxParam.Size.MaxBuf = ETHER_MAX_LEN; + rxParam.Initiator = 0; + rxParam.StartAddrSrc = (uint32)&(mpc5200.rfifo_data); + rxParam.IncrSrc = 0; + rxParam.SzSrc = sizeof(uint32_t); + rxParam.StartAddrDst = (uint32)NULL; + rxParam.IncrDst = sizeof(uint32_t); + rxParam.SzDst = sizeof(uint32_t); + rxTaskId = TaskSetup(TASK_FEC_RX,&rxParam ); + + /* + * Setup the TX task. + */ + txParam.NumBD = SDMA_BD_TX_NUM; + txParam.Size.MaxBuf = ETHER_MAX_LEN; + txParam.Initiator = 0; + txParam.StartAddrSrc = (uint32)NULL; + txParam.IncrSrc = sizeof(uint32_t); + txParam.SzSrc = sizeof(uint32_t); + txParam.StartAddrDst = (uint32)&(mpc5200.tfifo_data); + txParam.IncrDst = 0; + txParam.SzDst = sizeof(uint32_t); + + txTaskId = TaskSetup( TASK_FEC_TX, &txParam ); + +} + + +/* + * Initialize and start the device + */ +static void mpc5200_fec_init(void *arg) +{ + struct mpc5200_enet_struct *sc = (struct mpc5200_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + if(sc->txDaemonTid == 0) + { + /* + * 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"); + + bestcomm_glue_init(); + + mpc5200_sdma_task_setup(); + + /* + * Set up interrupts + */ + bestcomm_glue_irq_install(FEC_RECV_TASK_NO, + mpc5200_smartcomm_rx_irq_handler, + NULL); + bestcomm_glue_irq_install(FEC_XMIT_TASK_NO, + mpc5200_smartcomm_tx_irq_handler, + NULL); + /* mpc5200_fec_tx_bd_init(sc); */ + mpc5200_fec_rx_bd_init(sc); + + /* + * reset and Set up mpc5200 FEC hardware + */ + mpc5200_fec_initialize_hardware(sc); + /* + * Set priority of different initiators + */ + mpc5200.IPR0 = 7; /* always initiator */ + mpc5200.IPR3 = 6; /* eth rx initiator */ + mpc5200.IPR4 = 5; /* eth tx initiator */ + + /* + * Start driver tasks + */ + sc->txDaemonTid = rtems_bsdnet_newproc("FEtx", 4096, mpc5200_fec_txDaemon, sc); + sc->rxDaemonTid = rtems_bsdnet_newproc("FErx", 4096, mpc5200_fec_rxDaemon, sc); + /* + * Clear SmartDMA task interrupt pending bits. + */ + TaskIntClear( rxTaskId ); + + /* + * Enable the SmartDMA receive task. + */ + TaskStart( rxTaskId, 1, rxTaskId, 1 ); + TaskStart( txTaskId, 1, txTaskId, 1 ); + /* + * Enable FEC-Lite controller + */ + mpc5200.ecntrl |= (FEC_ECNTRL_OE | FEC_ECNTRL_EN); + + + } + + /* + * Set flags appropriately + */ + if(ifp->if_flags & IFF_PROMISC) + mpc5200.r_cntrl |= 0x08; + else + mpc5200.r_cntrl &= ~0x08; + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; +} + + +static void enet_stats (struct mpc5200_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); + +} + + +/* + * Driver ioctl handler + */ +static int mpc5200_fec_ioctl (struct ifnet *ifp, u_long command, caddr_t data) + { + struct mpc5200_enet_struct *sc = ifp->if_softc; + int error = 0; + + switch(command) + { + + case SIOCGIFADDR: + case SIOCSIFADDR: + + ether_ioctl(ifp, command, data); + + break; + + case SIOCSIFFLAGS: + + switch(ifp->if_flags & (IFF_UP | IFF_RUNNING)) + { + + case IFF_RUNNING: + + mpc5200_fec_off(sc); + + break; + + case IFF_UP: + + mpc5200_fec_init(sc); + + break; + + case IFF_UP | IFF_RUNNING: + + mpc5200_fec_off(sc); + mpc5200_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 MPC5200 fec driver to the system + */ +int rtems_mpc5200_fec_driver_attach(struct rtems_bsdnet_ifconfig *config) + { + struct mpc5200_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]; + 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, uboot_bdinfo_ptr->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 = mpc5200_fec_init; + ifp->if_ioctl = mpc5200_fec_ioctl; + ifp->if_start = mpc5200_fec_tx_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST; + /*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_mpc5200_fec_driver_attach_detach(struct rtems_bsdnet_ifconfig *config, int attaching) +{ + if (attaching) { + return rtems_mpc5200_fec_driver_attach(config); + } + else { + return 0; + } +} + + -- cgit v1.2.3