/*===============================================================*\
| Project: RTEMS generic MPC5200 BSP |
+-----------------------------------------------------------------+
| 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 |
\*===============================================================*/
/*
* RTEMS/TCPIP driver for MPC5200 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 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 <Darlene.Stewart@iit.nrc.ca>
* and Charles-Antoine Gauthier <charles.gauthier@iit.nrc.ca>
* Copyright (c) 1999, National Research Council of Canada
*
*/
#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/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <bsp.h>
#include "../irq/irq.h"
#include "../include/mpc5200.h"
#include <net/if_var.h>
#include <errno.h>
/* 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<<Bit))
#include "../bestcomm/bestcomm_api.h"
#include "../bestcomm/task_api/bestcomm_cntrl.h"
#include "../bestcomm/task_api/tasksetup_bdtable.h"
extern TaskBDIdxTable_t TaskBDIdxTable[MAX_TASKS];
static TaskId rxTaskId; /* SDMA RX task ID */
static TaskId txTaskId; /* SDMA TX task ID */
/*#define ETH_DEBUG*/
/*
* Number of interfaces supported by this driver
*/
#define NIFACES 1
/*
* 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 SDMA_BD_RX_NUM
#define TX_BUF_COUNT SDMA_BD_TX_NUM
#define TX_BD_PER_BUF 1
#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255")
/*
* 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
/*
* RTEMS event used to start transmit daemon.
* This must not be the same as INTERRUPT_EVENT.
*/
#define START_TRANSMIT_EVENT RTEMS_EVENT_2
/* Task number assignment */
#define FEC_RECV_TASK_NO TASK_FEC_RX
#define FEC_XMIT_TASK_NO TASK_FEC_TX
/* BD and parameters are stored in SRAM(refer to sdma.h) */
#define MPC5200_FEC_BD_BASE ETH_BD_BASE
/* FEC transmit watermark settings */
#define MPC5200_FEC_X_WMRK_64 0x0 /* or 0x1 */
#define MPC5200_FEC_X_WMRK_128 0x2
#define MPC5200_FEC_X_WMRK_192 0x3
/* RBD bits definitions */
#define MPC5200_FEC_RBD_EMPTY 0x8000 /* Buffer is empty */
#define MPC5200_FEC_RBD_WRAP 0x2000 /* Last BD in ring */
#define MPC5200_FEC_RBD_INT 0x1000 /* Interrupt */
#define MPC5200_FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */
#define MPC5200_FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */
#define MPC5200_FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */
#define MPC5200_FEC_RBD_MC 0x0040 /* The received frame is multicast frame */
#define MPC5200_FEC_RBD_LG 0x0020 /* Frame length violation */
#define MPC5200_FEC_RBD_NO 0x0010 /* Nonoctet align frame */
#define MPC5200_FEC_RBD_SH 0x0008 /* Short frame, FEC does not support SH and this bit is always cleared */
#define MPC5200_FEC_RBD_CR 0x0004 /* CRC error */
#define MPC5200_FEC_RBD_OV 0x0002 /* Receive FIFO overrun */
#define MPC5200_FEC_RBD_TR 0x0001 /* The receive frame is truncated */
#define MPC5200_FEC_RBD_ERR (MPC5200_FEC_RBD_LG | \
MPC5200_FEC_RBD_NO | \
MPC5200_FEC_RBD_CR | \
MPC5200_FEC_RBD_OV | \
MPC5200_FEC_RBD_TR)
/* TBD bits definitions */
#define MPC5200_FEC_TBD_READY 0x8000 /* Buffer is ready */
#define MPC5200_FEC_TBD_WRAP 0x2000 /* Last BD in ring */
#define MPC5200_FEC_TBD_INT 0x1000 /* Interrupt */
#define MPC5200_FEC_TBD_LAST 0x0800 /* Buffer is last in frame */
#define MPC5200_FEC_TBD_TC 0x0400 /* Transmit the CRC */
#define MPC5200_FEC_TBD_ABC 0x0200 /* Append bad CRC */
/* MII-related definitios */
#define MPC5200_FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */
#define MPC5200_FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */
#define MPC5200_FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */
#define MPC5200_FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */
#define MPC5200_FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */
#define MPC5200_FEC_MII_DATA_TA 0x00020000 /* Turnaround */
#define MPC5200_FEC_MII_DATA_DATAMSK 0x0000fff /* PHY data field */
#define MPC5200_FEC_MII_DATA_RA_SHIFT 0x12 /* MII Register address bits */
#define MPC5200_FEC_MII_DATA_PA_SHIFT 0x17 /* MII PHY address bits */
/* Receive & Transmit Buffer Descriptor definitions */
typedef struct mpc5200_buffer_desc_
{
volatile uint16_t status;
volatile uint16_t length;
volatile void *buffer;
} mpc5200_buffer_desc_t;
/*
* Device data
*/
struct mpc5200_enet_struct {
#if 0
struct ifnet ac_if;
#else
struct arpcom arpcom;
#endif
struct mbuf **rxMbuf;
struct mbuf **txMbuf;
int acceptBroadcast;
int rxBdCount;
int txBdCount;
int txBdHead;
int txBdTail;
int txBdActiveCount;
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;
};
uint8_t tx_shadow_buffer[TX_BUF_COUNT][(ETHER_MAX_LEN+3)&~3];
static struct mpc5200_enet_struct enet_driver[NIFACES];
extern int taskTable;
/*
* Function: mpc5200_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 mpc5200_fec_rx_bd_init(struct mpc5200_enet_struct *sc) {
int rxBdIndex;
struct mbuf *m;
struct ifnet *ifp = &sc->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;
}
}